Layout channel info to ensure routing uses cache lines well 2023-12-routing-dist-vec
authorMatt Corallo <git@bluematt.me>
Sat, 9 Dec 2023 21:22:52 +0000 (21:22 +0000)
committerMatt Corallo <git@bluematt.me>
Sun, 2 Jun 2024 00:44:40 +0000 (00:44 +0000)
Because we scan per-channel information in the hot inner loop of
our routefinding immediately after looking a channel up in a
`HashMap`, we end up spending a nontrivial portion of our
routefinding time waiting on memory to be read in.

While there is only so much we can do about that, ensuring the
channel information that we care about is sitting on one or
adjacent cache lines avoids paying that penalty twice. Thus, here
we manually lay out `ChannelInfo` and `ChannelUpdateInfo` and set
them to 128b and 32b alignment, respectively. This wastes some
space in memory in our network graph, but improves routing
performance in return.

215 files changed:
.github/workflows/build.yml
CHANGELOG.md
ci/check-cfg-flags.py
ci/check-compiles.sh
ci/ci-tests.sh
ci/rustfmt.sh
fuzz/Cargo.toml
fuzz/ci-fuzz.sh
fuzz/src/bin/base32_target.rs
fuzz/src/bin/bech32_parse_target.rs
fuzz/src/bin/bolt11_deser_target.rs [new file with mode: 0644]
fuzz/src/bin/chanmon_consistency_target.rs
fuzz/src/bin/chanmon_deser_target.rs
fuzz/src/bin/fromstr_to_netaddress_target.rs
fuzz/src/bin/full_stack_target.rs
fuzz/src/bin/gen_target.sh
fuzz/src/bin/indexedmap_target.rs
fuzz/src/bin/invoice_deser_target.rs
fuzz/src/bin/invoice_request_deser_target.rs
fuzz/src/bin/msg_accept_channel_target.rs
fuzz/src/bin/msg_accept_channel_v2_target.rs
fuzz/src/bin/msg_announcement_signatures_target.rs
fuzz/src/bin/msg_channel_announcement_target.rs
fuzz/src/bin/msg_channel_details_target.rs
fuzz/src/bin/msg_channel_ready_target.rs
fuzz/src/bin/msg_channel_reestablish_target.rs
fuzz/src/bin/msg_channel_update_target.rs
fuzz/src/bin/msg_closing_signed_target.rs
fuzz/src/bin/msg_commitment_signed_target.rs
fuzz/src/bin/msg_decoded_onion_error_packet_target.rs
fuzz/src/bin/msg_error_message_target.rs
fuzz/src/bin/msg_funding_created_target.rs
fuzz/src/bin/msg_funding_signed_target.rs
fuzz/src/bin/msg_gossip_timestamp_filter_target.rs
fuzz/src/bin/msg_init_target.rs
fuzz/src/bin/msg_node_announcement_target.rs
fuzz/src/bin/msg_open_channel_target.rs
fuzz/src/bin/msg_open_channel_v2_target.rs
fuzz/src/bin/msg_ping_target.rs
fuzz/src/bin/msg_pong_target.rs
fuzz/src/bin/msg_query_channel_range_target.rs
fuzz/src/bin/msg_query_short_channel_ids_target.rs
fuzz/src/bin/msg_reply_channel_range_target.rs
fuzz/src/bin/msg_reply_short_channel_ids_end_target.rs
fuzz/src/bin/msg_revoke_and_ack_target.rs
fuzz/src/bin/msg_shutdown_target.rs
fuzz/src/bin/msg_splice_ack_target.rs
fuzz/src/bin/msg_splice_locked_target.rs
fuzz/src/bin/msg_splice_target.rs
fuzz/src/bin/msg_stfu_target.rs
fuzz/src/bin/msg_tx_abort_target.rs
fuzz/src/bin/msg_tx_ack_rbf_target.rs
fuzz/src/bin/msg_tx_add_input_target.rs
fuzz/src/bin/msg_tx_add_output_target.rs
fuzz/src/bin/msg_tx_complete_target.rs
fuzz/src/bin/msg_tx_init_rbf_target.rs
fuzz/src/bin/msg_tx_remove_input_target.rs
fuzz/src/bin/msg_tx_remove_output_target.rs
fuzz/src/bin/msg_tx_signatures_target.rs
fuzz/src/bin/msg_update_add_htlc_target.rs
fuzz/src/bin/msg_update_fail_htlc_target.rs
fuzz/src/bin/msg_update_fail_malformed_htlc_target.rs
fuzz/src/bin/msg_update_fee_target.rs
fuzz/src/bin/msg_update_fulfill_htlc_target.rs
fuzz/src/bin/offer_deser_target.rs
fuzz/src/bin/onion_hop_data_target.rs
fuzz/src/bin/onion_message_target.rs
fuzz/src/bin/peer_crypt_target.rs
fuzz/src/bin/process_network_graph_target.rs
fuzz/src/bin/refund_deser_target.rs
fuzz/src/bin/router_target.rs
fuzz/src/bin/target_template.txt
fuzz/src/bin/zbase32_target.rs
fuzz/src/bolt11_deser.rs [new file with mode: 0644]
fuzz/src/chanmon_consistency.rs
fuzz/src/full_stack.rs
fuzz/src/invoice_request_deser.rs
fuzz/src/lib.rs
fuzz/src/offer_deser.rs
fuzz/src/onion_message.rs
fuzz/src/refund_deser.rs
fuzz/src/router.rs
fuzz/src/utils/test_persister.rs
fuzz/targets.h
lightning-background-processor/Cargo.toml
lightning-background-processor/src/lib.rs
lightning-block-sync/Cargo.toml
lightning-block-sync/src/convert.rs
lightning-block-sync/src/init.rs
lightning-block-sync/src/lib.rs
lightning-block-sync/src/poll.rs
lightning-block-sync/src/test_utils.rs
lightning-block-sync/src/utils.rs
lightning-custom-message/Cargo.toml
lightning-custom-message/src/lib.rs
lightning-invoice/Cargo.toml
lightning-invoice/fuzz/.gitignore [deleted file]
lightning-invoice/fuzz/Cargo.toml [deleted file]
lightning-invoice/fuzz/ci-fuzz.sh [deleted file]
lightning-invoice/fuzz/fuzz_targets/serde_data_part.rs [deleted file]
lightning-invoice/src/de.rs
lightning-invoice/src/lib.rs
lightning-invoice/src/payment.rs
lightning-invoice/src/ser.rs
lightning-invoice/src/utils.rs
lightning-invoice/tests/ser_de.rs
lightning-net-tokio/Cargo.toml
lightning-net-tokio/src/lib.rs
lightning-persister/Cargo.toml
lightning-persister/src/fs_store.rs
lightning-rapid-gossip-sync/Cargo.toml
lightning-rapid-gossip-sync/src/processing.rs
lightning-transaction-sync/Cargo.toml
lightning-transaction-sync/src/common.rs
lightning-transaction-sync/src/electrum.rs
lightning-transaction-sync/src/esplora.rs
lightning-transaction-sync/tests/integration_tests.rs
lightning/Cargo.toml
lightning/src/blinded_path/message.rs
lightning/src/blinded_path/mod.rs
lightning/src/blinded_path/payment.rs
lightning/src/blinded_path/utils.rs
lightning/src/chain/chaininterface.rs
lightning/src/chain/chainmonitor.rs
lightning/src/chain/channelmonitor.rs
lightning/src/chain/mod.rs
lightning/src/chain/onchaintx.rs
lightning/src/chain/package.rs
lightning/src/chain/transaction.rs
lightning/src/crypto/chacha20.rs
lightning/src/crypto/poly1305.rs
lightning/src/crypto/streams.rs
lightning/src/events/bump_transaction.rs
lightning/src/events/mod.rs
lightning/src/lib.rs
lightning/src/ln/async_signer_tests.rs
lightning/src/ln/blinded_payment_tests.rs
lightning/src/ln/chan_utils.rs
lightning/src/ln/chanmon_update_fail_tests.rs
lightning/src/ln/channel.rs
lightning/src/ln/channel_id.rs [deleted file]
lightning/src/ln/channel_keys.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/features.rs
lightning/src/ln/functional_test_utils.rs
lightning/src/ln/functional_tests.rs
lightning/src/ln/inbound_payment.rs
lightning/src/ln/interactivetxs.rs [new file with mode: 0644]
lightning/src/ln/max_payment_path_len_tests.rs [new file with mode: 0644]
lightning/src/ln/mod.rs
lightning/src/ln/monitor_tests.rs
lightning/src/ln/msgs.rs
lightning/src/ln/offers_tests.rs
lightning/src/ln/onion_payment.rs
lightning/src/ln/onion_route_tests.rs
lightning/src/ln/onion_utils.rs
lightning/src/ln/outbound_payment.rs
lightning/src/ln/payment_tests.rs
lightning/src/ln/peer_handler.rs
lightning/src/ln/priv_short_conf_tests.rs
lightning/src/ln/reload_tests.rs
lightning/src/ln/reorg_tests.rs
lightning/src/ln/script.rs
lightning/src/ln/shutdown_tests.rs
lightning/src/ln/types.rs [new file with mode: 0644]
lightning/src/ln/wire.rs
lightning/src/offers/invoice.rs
lightning/src/offers/invoice_error.rs
lightning/src/offers/invoice_request.rs
lightning/src/offers/merkle.rs
lightning/src/offers/offer.rs
lightning/src/offers/parse.rs
lightning/src/offers/payer.rs
lightning/src/offers/refund.rs
lightning/src/offers/signer.rs
lightning/src/offers/test_utils.rs
lightning/src/onion_message/functional_tests.rs
lightning/src/onion_message/messenger.rs
lightning/src/onion_message/offers.rs
lightning/src/onion_message/packet.rs
lightning/src/routing/gossip.rs
lightning/src/routing/mod.rs
lightning/src/routing/router.rs
lightning/src/routing/scoring.rs
lightning/src/routing/test_utils.rs
lightning/src/routing/utxo.rs
lightning/src/sign/ecdsa.rs
lightning/src/sign/mod.rs
lightning/src/sign/taproot.rs
lightning/src/sign/type_resolver.rs
lightning/src/sync/debug_sync.rs
lightning/src/util/base32.rs
lightning/src/util/config.rs
lightning/src/util/errors.rs
lightning/src/util/fuzz_wrappers.rs
lightning/src/util/indexed_map.rs
lightning/src/util/invoice.rs
lightning/src/util/logger.rs
lightning/src/util/macro_logger.rs
lightning/src/util/message_signing.rs
lightning/src/util/mod.rs
lightning/src/util/persist.rs
lightning/src/util/scid_utils.rs
lightning/src/util/ser.rs
lightning/src/util/ser_macros.rs
lightning/src/util/string.rs
lightning/src/util/sweep.rs [new file with mode: 0644]
lightning/src/util/test_channel_signer.rs
lightning/src/util/test_utils.rs
lightning/src/util/time.rs
lightning/src/util/transaction_utils.rs
lightning/src/util/wakers.rs
msrv-no-dev-deps-check/src/lib.rs
possiblyrandom/Cargo.toml
rustfmt_excluded_files

index f2f944ade5077f0d1152947af034453f287b0048..1a7c7f85cca0b54964a9bfd28110185547df80d7 100644 (file)
@@ -208,13 +208,10 @@ jobs:
       - name: Pin the regex dependency
         run: |
           cd fuzz && cargo update -p regex --precise "1.9.6" --verbose && cd ..
-          cd lightning-invoice/fuzz && cargo update -p regex --precise "1.9.6" --verbose
       - name: Sanity check fuzz targets on Rust ${{ env.TOOLCHAIN }}
-        run: cd fuzz && RUSTFLAGS="--cfg=fuzzing" cargo test --verbose --color always
+        run: cd fuzz && RUSTFLAGS="--cfg=fuzzing --cfg=secp256k1_fuzz --cfg=hashes_fuzz" cargo test --verbose --color always
       - name: Run fuzzers
         run: cd fuzz && ./ci-fuzz.sh && cd ..
-      - name: Run lightning-invoice fuzzers
-        run: cd lightning-invoice/fuzz && RUSTFLAGS="--cfg=fuzzing" cargo test --verbose && ./ci-fuzz.sh
 
   linting:
     runs-on: ubuntu-latest
index 3e473a22a1cd0b86fe1dc18d0c1c0f98ff58f6d2..f3108ae437ba19410da79ec30d117777e94479de 100644 (file)
@@ -1,3 +1,160 @@
+# 0.0.123 - May 08, 2024 - "BOLT12 Dust Sweeping"
+
+## API Updates
+
+ * To reduce risk of force-closures and improve HTLC reliability the default
+   dust exposure limit has been increased to
+   `MaxDustHTLCExposure::FeeRateMultiplier(10_000)`. Users with existing
+   channels might want to consider using
+   `ChannelManager::update_channel_config` to apply the new default (#3045).
+ * `ChainMonitor::archive_fully_resolved_channel_monitors` is now provided to
+   remove from memory `ChannelMonitor`s that have been fully resolved on-chain
+   and are now not needed. It uses the new `Persist::archive_persisted_channel`
+   to inform the storage layer that such a monitor should be archived (#2964).
+ * An `OutputSweeper` is now provided which will automatically sweep
+   `SpendableOutputDescriptor`s, retrying until the sweep confirms (#2825).
+ * After initiating an outbound channel, a peer disconnection no longer results
+   in immediate channel closure. Rather, if the peer is reconnected before the
+   channel times out LDK will automatically retry opening it (#2725).
+ * `PaymentPurpose` now has separate variants for BOLT12 payments, which
+   include fields from the `invoice_request` as well as the `OfferId` (#2970).
+ * `ChannelDetails` now includes a list of in-flight HTLCs (#2442).
+ * `Event::PaymentForwarded` now includes `skimmed_fee_msat` (#2858).
+ * The `hashbrown` dependency has been upgraded and the use of `ahash` as the
+   no-std hash table hash function has been removed. As a consequence, LDK's
+   `Hash{Map,Set}`s no longer feature several constructors when LDK is built
+   with no-std; see the `util::hash_tables` module instead. On platforms that
+   `getrandom` supports, setting the `possiblyrandom/getrandom` feature flag
+   will ensure hash tables are resistant to HashDoS attacks, though the
+   `possiblyrandom` crate should detect most common platforms (#2810, #2891).
+ * `ChannelMonitor`-originated requests to the `ChannelSigner` can now fail and
+   be retried using `ChannelMonitor::signer_unblocked` (#2816).
+ * `SpendableOutputDescriptor::to_psbt_input` now includes the `witness_script`
+   where available as well as new proprietary data which can be used to
+   re-derive some spending keys from the base key (#2761, #3004).
+ * `OutPoint::to_channel_id` has been removed in favor of
+   `ChannelId::v1_from_funding_outpoint` in preparation for v2 channels with a
+   different `ChannelId` derivation scheme (#2797).
+ * `PeerManager::get_peer_node_ids` has been replaced with `list_peers` and
+   `peer_by_node_id`, which provide more details (#2905).
+ * `Bolt11Invoice::get_payee_pub_key` is now provided (#2909).
+ * `Default[Message]Router` now take an `entropy_source` argument (#2847).
+ * `ClosureReason::HTLCsTimedOut` has been separated out from
+   `ClosureReason::HolderForceClosed` as it is the most common case (#2887).
+ * `ClosureReason::CooperativeClosure` is now split into
+   `{Counterparty,Locally}Initiated` variants (#2863).
+ * `Event::ChannelPending::channel_type` is now provided (#2872).
+ * `PaymentForwarded::{prev,next}_user_channel_id` are now provided (#2924).
+ * Channel init messages have been refactored towards V2 channels (#2871).
+ * `BumpTransactionEvent` now contains the channel and counterparty (#2873).
+ * `util::scid_utils` is now public, with some trivial utilities to examine
+   short channel ids (#2694).
+ * `DirectedChannelInfo::{source,target}` are now public (#2870).
+ * Bounds in `lightning-background-processor` were simplified by using
+   `AChannelManager` (#2963).
+ * The `Persist` impl for `KVStore` no longer requires `Sized`, allowing for
+   the use of `dyn KVStore` as `Persist` (#2883, #2976).
+ * `From<PaymentPreimage>` is now implemented for `PaymentHash` (#2918).
+ * `NodeId::from_slice` is now provided (#2942).
+ * `ChannelManager` deserialization may now fail with `DangerousValue` when
+    LDK's persistence API was violated (#2974).
+
+## Bug Fixes
+ * Excess fees on counterparty commitment transactions are now included in the
+   dust exposure calculation. This lines behavior up with some cases where
+   transaction fees can be burnt, making them effectively dust exposure (#3045).
+ * `Future`s used as an `std::...::Future` could grow in size unbounded if it
+   was never woken. For those not using async persistence and using the async
+   `lightning-background-processor`, this could cause a memory leak in the
+   `ChainMonitor` (#2894).
+ * Inbound channel requests that fail in
+   `ChannelManager::accept_inbound_channel` would previously have stalled from
+   the peer's perspective as no `error` message was sent (#2953).
+ * Blinded path construction has been tuned to select paths more likely to
+   succeed, improving BOLT12 payment reliability (#2911, #2912).
+ * After a reorg, `lightning-transaction-sync` could have failed to follow a
+   transaction that LDK needed information about (#2946).
+ * `RecipientOnionFields`' `custom_tlvs` are now propagated to recipients when
+   paying with blinded paths (#2975).
+ * `Event::ChannelClosed` is now properly generated and peers are properly
+   notified for all channels that as a part of a batch channel open fail to be
+   funded (#3029).
+ * In cases where user event processing is substantially delayed such that we
+   complete multiple round-trips with our peers before a `PaymentSent` event is
+   handled and then restart without persisting the `ChannelManager` after having
+   persisted a `ChannelMonitor[Update]`, on startup we may have `Err`d trying to
+   deserialize the `ChannelManager` (#3021).
+ * If a peer has relatively high latency, `PeerManager` may have failed to
+   establish a connection (#2993).
+ * `ChannelUpdate` messages broadcasted for our own channel closures are now
+   slightly more robust (#2731).
+ * Deserializing malformed BOLT11 invoices may have resulted in an integer
+   overflow panic in debug builds (#3032).
+ * In exceedingly rare cases (no cases of this are known), LDK may have created
+   an invalid serialization for a `ChannelManager` (#2998).
+ * Message processing latency handling BOLT12 payments has been reduced (#2881).
+ * Latency in processing `Event::SpendableOutputs` may be reduced (#3033).
+
+## Node Compatibility
+ * LDK's blinded paths were inconsistent with other implementations in several
+   ways, which have been addressed (#2856, #2936, #2945).
+ * LDK's messaging blinded paths now support the latest features which some
+   nodes may begin relying on soon (#2961).
+ * LDK's BOLT12 structs have been updated to support some last-minute changes to
+   the spec (#3017, #3018).
+ * CLN v24.02 requires the `gossip_queries` feature for all peers, however LDK
+   by default does not set it for those not using a `P2PGossipSync` (e.g. those
+   using RGS). This change was reverted in CLN v24.02.2 however for now LDK
+   always sets the `gossip_queries` feature. This change is expected to be
+   reverted in a future LDK release (#2959).
+
+## Security
+0.0.123 fixes a denial-of-service vulnerability which we believe to be reachable
+from untrusted input when parsing invalid BOLT11 invoices containing non-ASCII
+characters.
+ * BOLT11 invoices with non-ASCII characters in the human-readable-part may
+   cause an out-of-bounds read attempt leading to a panic (#3054). Note that all
+   BOLT11 invoices containing non-ASCII characters are invalid.
+
+In total, this release features 150 files changed, 19307 insertions, 6306
+deletions in 360 commits since 0.0.121 from 17 authors, in alphabetical order:
+
+ * Arik Sosman
+ * Duncan Dean
+ * Elias Rohrer
+ * Evan Feenstra
+ * Jeffrey Czyz
+ * Keyue Bao
+ * Matt Corallo
+ * Orbital
+ * Sergi Delgado Segura
+ * Valentine Wallace
+ * Willem Van Lint
+ * Wilmer Paulino
+ * benthecarman
+ * jbesraa
+ * olegkubrakov
+ * optout
+ * shaavan
+
+
+# 0.0.122 - Apr 09, 2024 - "That Which Is Untested Is Broken"
+
+## Bug Fixes
+ * `Route` objects did not successfully round-trip through de/serialization
+   since LDK 0.0.117, which has now been fixed (#2897).
+ * Correct deserialization of unknown future enum variants. This ensures
+   downgrades from future versions of LDK do not result in read failures or
+   corrupt reads in cases where enums are written (#2969).
+ * When hitting lnd bug 6039, our workaround previously resulted in
+   `ChannelManager` persistences on every round-trip with our peer. These
+   useless persistences are now skipped (#2937).
+
+In total, this release features 4 files changed, 99 insertions, 55
+deletions in 6 commits from 1 author, in alphabetical order:
+ * Matt Corallo
+
+
 # 0.0.121 - Jan 22, 2024 - "Unwraps are Bad"
 
 ## Bug Fixes
@@ -17,6 +174,7 @@ deletions in 4 commits from 2 authors, in alphabetical order:
  * Jeffrey Czyz
  * Matt Corallo
 
+
 # 0.0.120 - Jan 17, 2024 - "Unblinded Fuzzers"
 
 ## API Updates
@@ -65,6 +223,7 @@ deletions in 79 commits from 9 authors, in alphabetical order:
  * optout
  * shuoer86
 
+
 # 0.0.119 - Dec 15, 2023 - "Spring Cleaning for Christmas"
 
 ## API Updates
index 0cfa2023ee2b9844a07601548584d7c687f43e04..c33e8aa3a15015fb7e7c470def921d633cb2ceb9 100755 (executable)
@@ -82,6 +82,10 @@ def check_target_os(os):
 def check_cfg_tag(cfg):
     if cfg == "fuzzing":
         pass
+    elif cfg == "secp256k1_fuzz":
+        pass
+    elif cfg == "hashes_fuzz":
+        pass
     elif cfg == "test":
         pass
     elif cfg == "debug_assertions":
@@ -98,6 +102,8 @@ def check_cfg_tag(cfg):
         pass
     elif cfg == "dual_funding":
         pass
+    elif cfg == "splicing":
+        pass
     else:
         print("Bad cfg tag: " + cfg)
         assert False
index af88bceee01127667377c294c69429ae0c2e4630..2fe62be787ed60e95fe45af20651d3fc63843a42 100755 (executable)
@@ -5,6 +5,6 @@ echo Testing $(git log -1 --oneline)
 cargo check
 cargo doc
 cargo doc --document-private-items
-cd fuzz && RUSTFLAGS="--cfg=fuzzing" cargo check --features=stdin_fuzz
+cd fuzz && RUSTFLAGS="--cfg=fuzzing --cfg=secp256k1_fuzz --cfg=hashes_fuzz" cargo check --features=stdin_fuzz
 cd ../lightning && cargo check --no-default-features --features=no-std
 cd .. && RUSTC_BOOTSTRAP=1 RUSTFLAGS="--cfg=c_bindings" cargo check -Z avoid-dev-deps
index 5cae6d45de5f56a778fc95b2e78b5c5fd9f5ffb6..0dc654d8bedca04688e5cf5e8afcd248b4cc3587 100755 (executable)
@@ -177,3 +177,5 @@ RUSTFLAGS="--cfg=taproot" cargo test --verbose --color always -p lightning
 RUSTFLAGS="--cfg=async_signing" cargo test --verbose --color always -p lightning
 [ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
 RUSTFLAGS="--cfg=dual_funding" cargo test --verbose --color always -p lightning
+[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
+RUSTFLAGS="--cfg=splicing" cargo test --verbose --color always -p lightning
index dfc181dee7951926cad209df3404bf9a82ac8ec8..a1fdf6bd7f3282ff669944335e9d029d2a21989f 100755 (executable)
@@ -4,10 +4,16 @@ set -eox pipefail
 # Generate initial exclusion list
 #find . -name '*.rs' -type f |sort >rustfmt_excluded_files
 
+# The +rustversion syntax only works with rustup-installed rust toolchains,
+# not with any distro-provided ones. Thus, we check for a rustup install and
+# only pass +1.63.0 if we find one.
+VERS=""
+[ "$(which rustup)" != "" ] && VERS="+1.63.0"
+
 # Run fmt
 TMP_FILE=$(mktemp)
 find . -name '*.rs' -type f |sort >$TMP_FILE
 for file in $(comm -23 $TMP_FILE rustfmt_excluded_files); do
        echo "Checking formatting of $file"
-       rustfmt +1.63.0 --check $file
+       rustfmt $VERS --check $file
 done
index d87be2ef6a4e918ba41bb44dfc4d4f921ccf1094..4d9f7e0dea33697c8ec85cad904c737a2ffde4b0 100644 (file)
@@ -19,8 +19,10 @@ stdin_fuzz = []
 
 [dependencies]
 lightning = { path = "../lightning", features = ["regex", "hashbrown", "_test_utils"] }
+lightning-invoice = { path = "../lightning-invoice" }
 lightning-rapid-gossip-sync = { path = "../lightning-rapid-gossip-sync" }
-bitcoin = { version = "0.30.2", features = ["secp-lowmemory"] }
+bech32 = "0.9.1"
+bitcoin = { version = "0.31.2", features = ["secp-lowmemory"] }
 hex = { package = "hex-conservative", version = "0.1.1", default-features = false }
 
 afl = { version = "0.12", optional = true }
index 969505ca88d1bf1e227fd1eeee73c26c13dea741..5a5a8f7f803f8b73c9d96135a3ba1d2a5a5380e9 100755 (executable)
@@ -15,7 +15,11 @@ popd
 
 cargo install --color always --force honggfuzz --no-default-features
 sed -i 's/lto = true//' Cargo.toml
-HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz" cargo --color always hfuzz build
+
+export RUSTFLAGS="--cfg=secp256k1_fuzz --cfg=hashes_fuzz"
+export HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz"
+
+cargo --color always hfuzz build
 for TARGET in src/bin/*.rs; do
        FILENAME=$(basename $TARGET)
        FILE="${FILENAME%.*}"
@@ -28,7 +32,7 @@ for TARGET in src/bin/*.rs; do
                HFUZZ_RUN_ARGS="$HFUZZ_RUN_ARGS -N1000000"
        fi
        export HFUZZ_RUN_ARGS
-       HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz" cargo --color always hfuzz run $FILE
+       cargo --color always hfuzz run $FILE
        if [ -f hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT ]; then
                cat hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT
                for CASE in hfuzz_workspace/$FILE/SIG*; do
index a7951c77004ca1dda65532264b04150b681df298..cb8620df36f948c6f8148c7dd4de14fac8d8561c 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::base32::*;
 
index 629112f9fef0cd21617a4e2af86004ce8dc8e9fc..d05ad98191b9da00a816cb21bca1a8c4b5884652 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::bech32_parse::*;
 
diff --git a/fuzz/src/bin/bolt11_deser_target.rs b/fuzz/src/bin/bolt11_deser_target.rs
new file mode 100644 (file)
index 0000000..30091c3
--- /dev/null
@@ -0,0 +1,119 @@
+// 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");
+
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::bolt11_deser::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+       fuzz!(|data| {
+               bolt11_deser_run(data.as_ptr(), data.len());
+       });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+       loop {
+               fuzz!(|data| {
+                       bolt11_deser_run(data.as_ptr(), data.len());
+               });
+       }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+       bolt11_deser_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();
+       bolt11_deser_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];
+               bolt11_deser_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/bolt11_deser") {
+               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 || {
+                                               bolt11_deser_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!();
+       }
+}
index 25fe23b58caeb4e1fd5c29fd8894833ec70255ef..0b03b43c8f60b80660871ed62f02910c3ba9b1c4 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::chanmon_consistency::*;
 
index 9e9b17626768388ed6d4fefa369a40c09c2b3769..40bbf260df276d1c2f6fa885bfffee59b4e15337 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::chanmon_deser::*;
 
index 29c984e60160a9c29a6979b0aac92efdb157ee3a..27f7d8ae9ee29e8a098980c226e6c1a05bf5c210 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::fromstr_to_netaddress::*;
 
index bebb7fdcd81ebdd2f558154b661a4a7c95a77f35..1f5238940e5baefd22a153faf12dd084d2a2ccea 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::full_stack::*;
 
index 62381622f6b58bfddfd7da27b11e7aee10206a1e..f124c1e0ef03bd710417cbebf0b770f81df8094e 100755 (executable)
@@ -13,6 +13,7 @@ GEN_TEST full_stack
 GEN_TEST invoice_deser
 GEN_TEST invoice_request_deser
 GEN_TEST offer_deser
+GEN_TEST bolt11_deser
 GEN_TEST onion_message
 GEN_TEST peer_crypt
 GEN_TEST process_network_graph
index 238566d546553ee48da7c731b8e247808a94b392..93f7a012ff743cd2544cbc2a00fdb5a43b3ba3b8 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::indexedmap::*;
 
index 06dbbe3078cac7faa347ce234fbfa00d78d1fa18..14aea952714ba95a998b1ec7a776f59cd7703284 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::invoice_deser::*;
 
index 97741ff3ade27b7f30a09ea42b1da5464d2c09db..c86dbb1c27c6ba7322c1c35d4390120d9d991d62 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::invoice_request_deser::*;
 
index 0018d09abd3986af33219eb422b52768cacd15a3..43ae124b428cda6de6b0bc269d6c79e69879dea6 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_accept_channel::*;
 
index 354a5a8c4b84b45769bf593d4d8352c08e59fcd8..012c1b24ebc6f38c25c2e63a553886d571051c9b 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_accept_channel_v2::*;
 
index 593908f41751904d7dd8e36c299be5a9ae5507db..7608f10458020d73ff4a2f1c3d2eb5475ddd3eb0 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_announcement_signatures::*;
 
index f9946314254b7c5d9faa9c06d15464ee52d16bbf..cf4c1462703963f247f23302bcc157a809417c1a 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_channel_announcement::*;
 
index cb5021aedfa5e70e18c1d8133c28e3a5889bdf71..4040a7c00844f8a831f4c4343926af22ba842537 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_channel_details::*;
 
index d11068b7342a9679f4935cfb4ce7c7d34bf3b517..f8cfef2dff4f652361a1398416edf823af8178d2 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_channel_ready::*;
 
index ba1a76d9941eab3e1b596afc619d1395f1d3b83e..3aa6dc231d2baaf133b878b7de659c2ba78eeeda 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_channel_reestablish::*;
 
index 0198ddc0092e4fe60962c58b484b48186c7adb47..bb4902dcfc059283c96bbf70fe1b445395252b39 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_channel_update::*;
 
index abd69fc95029f3e3b689a815b4035a18e7ea1924..0985ab5228779fd1dde910e08bd871d42d47fc1f 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_closing_signed::*;
 
index 0324301fea79b554e4d50d8e3c7e32ae26c2b8b0..f0b26cdc5615e53c83561a2d35e01b32b192b5a4 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_commitment_signed::*;
 
index 1f99bab2bbb82675937001e5cbb192752c9d66b8..5801fbef62bec5224107e57d18a131d5ca464033 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_decoded_onion_error_packet::*;
 
index 8609bf65ada5f783660b3807e455625d92d28d9b..c36e553bfb399a75c280c982527f661c41a3b5e0 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_error_message::*;
 
index 51ac425d7e5899756f6aa2ae28bea8ae3f812c70..1787e9ddb24c8f585486cfde02bc0575a1519797 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_funding_created::*;
 
index bfee1f5aee934cfe4dd2084231d4ea9beda37cb8..1f0da2a8eaa1108f90def7cfbaaeb4e4b0007693 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_funding_signed::*;
 
index aa94f2001cfaab1bd5782c4d75fb6977a2a51b8f..fe41f3ddd3afda4cdc80f78e3d4246a8ae68d345 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_gossip_timestamp_filter::*;
 
index 342ceaf3f796b5c4378c3637b226f10c14800157..b4872cc155bbe41bbc4e37ce5bdfc836b64851c5 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_init::*;
 
index 32012633747ed1ce9d6bd3f9d16697a0d20a3580..a9fe444deff9e92e4f1d352efe1826caf8806b3d 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_node_announcement::*;
 
index cee4c0d07efedd4c4342adf9fe9a4ebb98482f44..ff623cda998a78da8ce038b87102ef7b0b783ced 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_open_channel::*;
 
index c7949bf9d8d94ddcb5a29a090e2943c4d3e70e2c..dfc901f7ac84f4f5aca1633730371692b22765d0 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_open_channel_v2::*;
 
index 19e8abe24f9a7bdd19d2f2be50beca13a2f9c0fb..bf7c553541d1432fcbc2192f9c790dd34c26c4d2 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_ping::*;
 
index 429550bd1864df484f24e6e0e7376be3328dc66b..a8e4285d1710a5d19fbbef64b59004dcf41e4ff3 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_pong::*;
 
index c2ce1fd5f52195130806f998d765ea08e8a70d7e..e07a046cb38d3eff4409eaf447848c24267f5099 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_query_channel_range::*;
 
index af3d37065e5655f58ac45337ecc0a6476ca318b6..25fcf311ee1e8ba3ab158f3470aee9a0bdd8d630 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_query_short_channel_ids::*;
 
index 0cddbcaf83de9a796f5c7bd680e5258252c754a0..a0b54f691daaa0ca5e30b482d494838e5e8555ba 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_reply_channel_range::*;
 
index d802299b28102e6ca62620857d0e642916d878cd..464641e5b5495b67f17df1b80aff011b1acd3f65 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_reply_short_channel_ids_end::*;
 
index d72d63cc32723eab28d05dcc86a98bc3fa9eb312..be20070b012f8e407a8c63ea01b9f6dd88a3eb4a 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_revoke_and_ack::*;
 
index 1959a8cfa858c9fff0558779fef9710b23ee74eb..9a4cbeb639e0e4765eda0ee792e3c360a384e779 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_shutdown::*;
 
index cff5675e1914fa292727522af1744c33731dac15..15b546b202cb2f877f66b863865bba58cd338181 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_splice_ack::*;
 
index ea5a49bc16dd0428126d3a31a819b44e65032e2b..afc6b1465796877dcf770fa49a531684783c1252 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_splice_locked::*;
 
index bff4dffbbc21e3f7fc64587dcb899530e81c1612..475c59971716bea3ebf64f1a295a5fcf512cf504 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_splice::*;
 
index 1cabf8ded487b03dc7da0ae5159d9f9ac1350399..9b75073b8256fb49c558093184a7ca2b9754bc5e 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_stfu::*;
 
index 66783563ae2148b489d1a344e15ccab17ea9fdbc..bbfd4ab8163a918f18a9dd3b3b446efa1bbe0e43 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_tx_abort::*;
 
index 2e6aaedc21e17167d7c66ac8e13c544813208be5..1d22dcea4b128a05c3c8d87d651771200eda39b8 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_tx_ack_rbf::*;
 
index 1da8bbd935918fddc690e56b15500b38848ff1db..7a858880ece63e8cc68c62430b823ce6190052f1 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_tx_add_input::*;
 
index a06d51aad6fd069ad6012ed8d8fca73bbbb9d633..2fcac4bebae2baea04f0924a2a1ec3195d772c0a 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_tx_add_output::*;
 
index 5bb2f855aff87fe2d5b127232c069fe0a3e457e0..9856c2cdf4c3adfb7bfa5222687ccbc4a3891555 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_tx_complete::*;
 
index 74556abb123754d1c57200ceff91516f8002e01e..88657e1105a738a53b03a2841df926a06a5c7887 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_tx_init_rbf::*;
 
index d6b9cff281518de34e42e982d5af6e034902f90b..02801865f6bf5bd4f4684fcab806d1d4be5e5108 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_tx_remove_input::*;
 
index 2c6b17d8178e439b4879e8715be8be946d3c5c61..4e1398135b89b367349bd2672d5faf3a37b70df2 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_tx_remove_output::*;
 
index ee650ec2b38aef9bdf6a11e306a9e6acbdd81a18..abb3ab82ac5ba2e0d7103c7f690f5f2954b9f522 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_tx_signatures::*;
 
index 47faa7b1bbb989b1cb379fbddface77d5bdd4bdd..ae8ba13a25d4a0573e58b00342b9ba425a9cefe9 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_update_add_htlc::*;
 
index 0c47190f72ced5ff645d8c2592190bfaf5c22d47..8767c7a31adb1519009ac4c91ba546a2395ec833 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_update_fail_htlc::*;
 
index 917fad9ce600d4f315d2cf968b99fdc7349f7238..1d5d7daf2f2f2054b9e1577f860607cee84c39d8 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_update_fail_malformed_htlc::*;
 
index f7baec58a170141d9b0d7c7ed5d986fd65c10b6c..207df698f6a16edbd7ccc4cd20e9ab841843d515 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_update_fee::*;
 
index 23197b38eec8704e4faee85eb04e988131e4d8fa..f8e2653591056985a4db9b1f503388a6f19a3b6e 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::msg_targets::msg_update_fulfill_htlc::*;
 
index 49563b1029180235ae0e6a34fd37b22e4f8b0af1..64b2c4460ddb2f19afceb41050fa5c48e48c21d8 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::offer_deser::*;
 
index b8a357229bf2907f343b541378921f40c0fa745f..0043cee63cee0290f4dd9e6a11b35241d2a7a9db 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::onion_hop_data::*;
 
index e9bcf590da142ac8c5d562406d6c8b2be6e6f6d1..1c28f5608ec2d5371701e54187423957b4f15ba9 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::onion_message::*;
 
index c7cbe8c2c2f30389e34265176cca430d0e2ef19c..8690f233251082a818b2bb21f7a7c727ff445e5d 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::peer_crypt::*;
 
index 380efdff447913ad8bdd2570436a619b17380fa9..5b5d8f8161544fb5adda1e679255d7ff1d0f1305 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::process_network_graph::*;
 
index c9857783467457d0d951375cb51bc383d684b0fa..fca53dd2deca93f3587bc3a8f80282037757d0d0 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::refund_deser::*;
 
index 2082f115ae0572f39b9662f6cf64b762d4e9078d..f9808ebc80f0392983219e2a0f7143fb908ee66b 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::router::*;
 
index 7b0ec0eebce154ee3454201b688024f50e2885d3..7603ccb271983f64d6d0e5367866f8869b75920d 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::TARGET_MOD::*;
 
index 1553d6d2b66d60cab4cc899e0f7494abfccf6e5b..e0ad2c5bcd1b303ed6da64965f62bf01ff5e8e50 100644 (file)
 #[cfg(not(fuzzing))]
 compile_error!("Fuzz targets need cfg=fuzzing");
 
+#[cfg(not(hashes_fuzz))]
+compile_error!("Fuzz targets need cfg=hashes_fuzz");
+
+#[cfg(not(secp256k1_fuzz))]
+compile_error!("Fuzz targets need cfg=secp256k1_fuzz");
+
 extern crate lightning_fuzz;
 use lightning_fuzz::zbase32::*;
 
diff --git a/fuzz/src/bolt11_deser.rs b/fuzz/src/bolt11_deser.rs
new file mode 100644 (file)
index 0000000..63d869c
--- /dev/null
@@ -0,0 +1,75 @@
+// 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.
+
+use crate::utils::test_logger;
+use bech32::{u5, FromBase32, ToBase32};
+use bitcoin::secp256k1::{Secp256k1, SecretKey};
+use lightning_invoice::{
+       Bolt11Invoice, RawBolt11Invoice, RawDataPart, RawHrp, RawTaggedField, TaggedField,
+};
+use std::str::FromStr;
+
+#[inline]
+pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+       // Read a fake HRP length byte
+       let hrp_len = std::cmp::min(*data.get(0).unwrap_or(&0) as usize, data.len());
+       if let Ok(s) = std::str::from_utf8(&data[..hrp_len]) {
+               let hrp = match RawHrp::from_str(s) {
+                       Ok(hrp) => hrp,
+                       Err(_) => return,
+               };
+               let bech32 =
+                       data.iter().skip(hrp_len).map(|x| u5::try_from_u8(x % 32).unwrap()).collect::<Vec<_>>();
+               let invoice_data = match RawDataPart::from_base32(&bech32) {
+                       Ok(invoice) => invoice,
+                       Err(_) => return,
+               };
+
+               // Our data encoding is not worse than the input
+               assert!(invoice_data.to_base32().len() <= bech32.len());
+
+               // Our data serialization is loss-less
+               assert_eq!(
+                       RawDataPart::from_base32(&invoice_data.to_base32())
+                               .expect("faild parsing out own encoding"),
+                       invoice_data
+               );
+
+               if invoice_data.tagged_fields.iter().any(|field| {
+                       matches!(field, RawTaggedField::KnownSemantics(TaggedField::PayeePubKey(_)))
+               }) {
+                       // We could forge a signature using the fact that signing is insecure in fuzz mode, but
+                       // easier to just skip and rely on the fact that no-PayeePubKey invoices do pubkey
+                       // recovery
+                       return;
+               }
+
+               let raw_invoice = RawBolt11Invoice { hrp, data: invoice_data };
+               let signed_raw_invoice = match raw_invoice.sign(|hash| {
+                       let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
+                       Ok::<_, ()>(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key))
+               }) {
+                       Ok(inv) => inv,
+                       Err(_) => return,
+               };
+
+               if let Ok(invoice) = Bolt11Invoice::from_signed(signed_raw_invoice) {
+                       invoice.amount_milli_satoshis();
+               }
+       }
+}
+
+pub fn bolt11_deser_test<Out: test_logger::Output>(data: &[u8], out: Out) {
+       do_test(data, out);
+}
+
+#[no_mangle]
+pub extern "C" fn bolt11_deser_run(data: *const u8, datalen: usize) {
+       do_test(unsafe { std::slice::from_raw_parts(data, datalen) }, test_logger::DevNull {});
+}
index 36e7cea8a2215e1414af4ef0f46a082401732c03..ec808863224075f751b7dee35b66e5776e3cae5a 100644 (file)
 //! send-side handling is correct, other peers. We consider it a failure if any action results in a
 //! channel being force-closed.
 
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::constants::genesis_block;
 use bitcoin::blockdata::transaction::{Transaction, TxOut};
 use bitcoin::blockdata::script::{Builder, ScriptBuf};
 use bitcoin::blockdata::opcodes;
 use bitcoin::blockdata::locktime::absolute::LockTime;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
+use bitcoin::transaction::Version;
 
+use bitcoin::WPubkeyHash;
 use bitcoin::hashes::Hash as TraitImport;
 use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::hashes::sha256d::Hash as Sha256dHash;
-use bitcoin::hash_types::{BlockHash, WPubkeyHash};
+use bitcoin::hash_types::BlockHash;
 
 use lightning::blinded_path::BlindedPath;
+use lightning::blinded_path::message::ForwardNode;
 use lightning::blinded_path::payment::ReceiveTlvs;
 use lightning::chain;
 use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, chainmonitor, channelmonitor, Confirm, Watch};
@@ -70,7 +74,7 @@ use std::cmp::{self, Ordering};
 use std::sync::{Arc,Mutex};
 use std::sync::atomic;
 use std::io::Cursor;
-use bitcoin::bech32::u5;
+use bech32::u5;
 
 const MAX_FEE: u32 = 10_000;
 struct FuzzEstimator {
@@ -84,7 +88,7 @@ impl FeeEstimator for FuzzEstimator {
                // Background feerate which is <= the minimum Normal feerate.
                match conf_target {
                        ConfirmationTarget::OnChainSweep => MAX_FEE,
-                       ConfirmationTarget::ChannelCloseMinimum|ConfirmationTarget::AnchorChannelFee|ConfirmationTarget::MinAllowedAnchorChannelRemoteFee|ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee => 253,
+                       ConfirmationTarget::ChannelCloseMinimum|ConfirmationTarget::AnchorChannelFee|ConfirmationTarget::MinAllowedAnchorChannelRemoteFee|ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee|ConfirmationTarget::OutputSpendingFee => 253,
                        ConfirmationTarget::NonAnchorChannelFee => cmp::min(self.ret_val.load(atomic::Ordering::Acquire), MAX_FEE),
                }
        }
@@ -119,7 +123,7 @@ impl MessageRouter for FuzzRouter {
        }
 
        fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
-               &self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
+               &self, _recipient: PublicKey, _peers: Vec<ForwardNode>, _secp_ctx: &Secp256k1<T>,
        ) -> Result<Vec<BlindedPath>, ()> {
                unreachable!()
        }
@@ -244,7 +248,7 @@ impl NodeSigner for KeyProvider {
        }
 
        fn sign_gossip_message(&self, msg: lightning::ln::msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
-               let msg_hash = Message::from_slice(&Sha256dHash::hash(&msg.encode()[..])[..]).map_err(|_| ())?;
+               let msg_hash = Message::from_digest(Sha256dHash::hash(&msg.encode()[..]).to_byte_array());
                let secp_ctx = Secp256k1::signing_only();
                Ok(secp_ctx.sign_ecdsa(&msg_hash, &self.node_secret))
        }
@@ -588,8 +592,8 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
                                let events = $source.get_and_clear_pending_events();
                                assert_eq!(events.len(), 1);
                                if let events::Event::FundingGenerationReady { ref temporary_channel_id, ref channel_value_satoshis, ref output_script, .. } = events[0] {
-                                       let tx = Transaction { version: $chan_id, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
-                                               value: *channel_value_satoshis, script_pubkey: output_script.clone(),
+                                       let tx = Transaction { version: Version($chan_id), lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
+                                               value: Amount::from_sat(*channel_value_satoshis), script_pubkey: output_script.clone(),
                                        }]};
                                        funding_output = OutPoint { txid: tx.txid(), index: 0 };
                                        $source.funding_transaction_generated(&temporary_channel_id, &$dest.get_our_node_id(), tx.clone()).unwrap();
index e128d91810a0ebdb9d548f99b232b50b4a6a7fed..30508fe532473c2222e40cad7a96456e8dd1f117 100644 (file)
 //! 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::amount::Amount;
 use bitcoin::blockdata::constants::genesis_block;
 use bitcoin::blockdata::transaction::{Transaction, TxOut};
 use bitcoin::blockdata::script::{Builder, ScriptBuf};
 use bitcoin::blockdata::opcodes;
 use bitcoin::blockdata::locktime::absolute::LockTime;
 use bitcoin::consensus::encode::deserialize;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
+use bitcoin::transaction::Version;
 
+use bitcoin::WPubkeyHash;
 use bitcoin::hashes::hex::FromHex;
 use bitcoin::hashes::Hash as _;
 use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::hashes::sha256d::Hash as Sha256dHash;
-use bitcoin::hash_types::{Txid, BlockHash, WPubkeyHash};
+use bitcoin::hash_types::{Txid, BlockHash};
 
 use lightning::blinded_path::BlindedPath;
+use lightning::blinded_path::message::ForwardNode;
 use lightning::blinded_path::payment::ReceiveTlvs;
 use lightning::chain;
 use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen};
@@ -68,7 +72,7 @@ use std::convert::TryInto;
 use std::cmp;
 use std::sync::{Arc, Mutex};
 use std::sync::atomic::{AtomicU64,AtomicUsize,Ordering};
-use bitcoin::bech32::u5;
+use bech32::u5;
 
 #[inline]
 pub fn slice_to_be16(v: &[u8]) -> u16 {
@@ -157,7 +161,7 @@ impl MessageRouter for FuzzRouter {
        }
 
        fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
-               &self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
+               &self, _recipient: PublicKey, _peers: Vec<ForwardNode>, _secp_ctx: &Secp256k1<T>,
        ) -> Result<Vec<BlindedPath>, ()> {
                unreachable!()
        }
@@ -349,7 +353,7 @@ impl NodeSigner for KeyProvider {
        }
 
        fn sign_gossip_message(&self, msg: lightning::ln::msgs::UnsignedGossipMessage) -> Result<Signature, ()> {
-               let msg_hash = Message::from_slice(&Sha256dHash::hash(&msg.encode()[..])[..]).map_err(|_| ())?;
+               let msg_hash = Message::from_digest(Sha256dHash::hash(&msg.encode()[..]).to_byte_array());
                let secp_ctx = Secp256k1::signing_only();
                Ok(secp_ctx.sign_ecdsa(&msg_hash, &self.node_secret))
        }
@@ -650,11 +654,11 @@ pub fn do_test(mut data: &[u8], logger: &Arc<dyn Logger>) {
                                }
                        },
                        10 => {
-                               let mut tx = Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
+                               let mut tx = Transaction { version: Version(0), lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
                                let mut channels = Vec::new();
                                for funding_generation in pending_funding_generation.drain(..) {
                                        let txout = TxOut {
-                                               value: funding_generation.2, script_pubkey: funding_generation.3,
+                                               value: Amount::from_sat(funding_generation.2), script_pubkey: funding_generation.3,
                                        };
                                        if !tx.output.contains(&txout) {
                                                tx.output.push(txout);
@@ -664,7 +668,7 @@ pub fn do_test(mut data: &[u8], logger: &Arc<dyn Logger>) {
                                // Once we switch to V2 channel opens we should be able to drop this entirely as
                                // channel_ids no longer change when we set the funding tx.
                                'search_loop: loop {
-                                       if tx.version > 0xff {
+                                       if tx.version.0 > 0xff {
                                                break;
                                        }
                                        let funding_txid = tx.txid();
@@ -672,15 +676,15 @@ pub fn do_test(mut data: &[u8], logger: &Arc<dyn Logger>) {
                                                let outpoint = OutPoint { txid: funding_txid, index: 0 };
                                                for chan in channelmanager.list_channels() {
                                                        if chan.channel_id == ChannelId::v1_from_funding_outpoint(outpoint) {
-                                                               tx.version += 1;
+                                                               tx.version = Version(tx.version.0 + 1);
                                                                continue 'search_loop;
                                                        }
                                                }
                                                break;
                                        }
-                                       tx.version += 1;
+                                       tx.version = Version(tx.version.0 + 1);
                                }
-                               if tx.version <= 0xff && !channels.is_empty() {
+                               if tx.version.0 <= 0xff && !channels.is_empty() {
                                        let chans = channels.iter().map(|(a, b)| (a, b)).collect::<Vec<_>>();
                                        if let Err(e) = channelmanager.batch_funding_transaction_generated(&chans, tx.clone()) {
                                                // It's possible the channel has been closed in the mean time, but any other
@@ -713,11 +717,11 @@ pub fn do_test(mut data: &[u8], logger: &Arc<dyn Logger>) {
                                } else {
                                        let txres: Result<Transaction, _> = deserialize(get_slice!(txlen));
                                        if let Ok(tx) = txres {
-                                               let mut output_val = 0;
+                                               let mut output_val = Amount::ZERO;
                                                for out in tx.output.iter() {
-                                                       if out.value > 21_000_000_0000_0000 { return; }
+                                                       if out.value > Amount::MAX_MONEY { return; }
                                                        output_val += out.value;
-                                                       if output_val > 21_000_000_0000_0000 { return; }
+                                                       if output_val > Amount::MAX_MONEY { return; }
                                                }
                                                loss_detector.connect_block(&[tx]);
                                        } else {
@@ -971,6 +975,8 @@ mod tests {
 
                // create the funding transaction (client should send funding_created now)
                ext_from_hex("0a", &mut test);
+               // Two feerate requests to check the dust exposure on the initial commitment tx
+               ext_from_hex("00fd00fd", &mut test);
 
                // inbound read from peer id 1 of len 18
                ext_from_hex("030112", &mut test);
@@ -1019,6 +1025,9 @@ mod tests {
                // end of update_add_htlc from 0 to 1 via client and mac
                ext_from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ab00000000000000000000000000000000000000000000000000000000000000 03000000000000000000000000000000", &mut test);
 
+               // Two feerate requests to check dust exposure
+               ext_from_hex("00fd00fd", &mut test);
+
                // inbound read from peer id 0 of len 18
                ext_from_hex("030012", &mut test);
                // message header indicating message length 100
@@ -1040,6 +1049,8 @@ mod tests {
 
                // process the now-pending HTLC forward
                ext_from_hex("07", &mut test);
+               // Two feerate requests to check dust exposure
+               ext_from_hex("00fd00fd", &mut test);
                // client now sends id 1 update_add_htlc and commitment_signed (CHECK 7: UpdateHTLCs event for node 03020000 with 1 HTLCs for channel 3f000000)
 
                // we respond with commitment_signed then revoke_and_ack (a weird, but valid, order)
@@ -1115,6 +1126,9 @@ mod tests {
                // end of update_add_htlc from 0 to 1 via client and mac
                ext_from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ab00000000000000000000000000000000000000000000000000000000000000 03000000000000000000000000000000", &mut test);
 
+               // Two feerate requests to check dust exposure
+               ext_from_hex("00fd00fd", &mut test);
+
                // now respond to the update_fulfill_htlc+commitment_signed messages the client sent to peer 0
                // inbound read from peer id 0 of len 18
                ext_from_hex("030012", &mut test);
@@ -1146,6 +1160,10 @@ mod tests {
 
                // process the now-pending HTLC forward
                ext_from_hex("07", &mut test);
+
+               // Two feerate requests to check dust exposure
+               ext_from_hex("00fd00fd", &mut test);
+
                // client now sends id 1 update_add_htlc and commitment_signed (CHECK 7 duplicate)
                // we respond with revoke_and_ack, then commitment_signed, then update_fail_htlc
 
@@ -1243,6 +1261,9 @@ mod tests {
                // end of update_add_htlc from 0 to 1 via client and mac
                ext_from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 5300000000000000000000000000000000000000000000000000000000000000 03000000000000000000000000000000", &mut test);
 
+               // Two feerate requests to check dust exposure
+               ext_from_hex("00fd00fd", &mut test);
+
                // inbound read from peer id 0 of len 18
                ext_from_hex("030012", &mut test);
                // message header indicating message length 164
@@ -1264,6 +1285,8 @@ mod tests {
 
                // process the now-pending HTLC forward
                ext_from_hex("07", &mut test);
+               // Two feerate requests to check dust exposure
+               ext_from_hex("00fd00fd", &mut test);
                // client now sends id 1 update_add_htlc and commitment_signed (CHECK 7 duplicate)
 
                // connect a block with one transaction of len 125
index 414ce1cdd1cd2c17431d68ea9c1015495b9e074a..1381f6697d19a69c481a7df7937beba4ab952713 100644 (file)
@@ -7,10 +7,11 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
-use bitcoin::secp256k1::{KeyPair, Parity, PublicKey, Secp256k1, SecretKey, self};
+use bitcoin::secp256k1::{Keypair, Parity, PublicKey, Secp256k1, SecretKey, self};
 use crate::utils::test_logger;
 use core::convert::TryFrom;
 use lightning::blinded_path::BlindedPath;
+use lightning::blinded_path::message::ForwardNode;
 use lightning::sign::EntropySource;
 use lightning::ln::PaymentHash;
 use lightning::ln::features::BlindedHopFeatures;
@@ -27,7 +28,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
                assert_eq!(data, bytes);
 
                let secp_ctx = Secp256k1::new();
-               let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+               let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
                let mut buffer = Vec::new();
 
                if let Ok(unsigned_invoice) = build_response(&invoice_request, &secp_ctx) {
@@ -73,9 +74,19 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
        invoice_request: &InvoiceRequest, secp_ctx: &Secp256k1<T>
 ) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
        let entropy_source = Randomness {};
+       let intermediate_nodes = [
+               [
+                       ForwardNode { node_id: pubkey(43), short_channel_id: None },
+                       ForwardNode { node_id: pubkey(44), short_channel_id: None },
+               ],
+               [
+                       ForwardNode { node_id: pubkey(45), short_channel_id: None },
+                       ForwardNode { node_id: pubkey(46), short_channel_id: None },
+               ],
+       ];
        let paths = vec![
-               BlindedPath::new_for_message(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
-               BlindedPath::new_for_message(&[pubkey(45), pubkey(46), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
+               BlindedPath::new_for_message(&intermediate_nodes[0], pubkey(42), &entropy_source, secp_ctx).unwrap(),
+               BlindedPath::new_for_message(&intermediate_nodes[1], pubkey(42), &entropy_source, secp_ctx).unwrap(),
        ];
 
        let payinfo = vec![
index 5b5cd69cf9681ac2d1fa5c8b75a52eebbd0447a5..6efdd94dfcb433650c5480b92e4ceabb81909f4b 100644 (file)
@@ -22,6 +22,7 @@ pub mod indexedmap;
 pub mod invoice_deser;
 pub mod invoice_request_deser;
 pub mod offer_deser;
+pub mod bolt11_deser;
 pub mod onion_message;
 pub mod peer_crypt;
 pub mod process_network_graph;
index 5aad4642e461f6477a55591cf2239507882cd86a..deaa6f3588b7bcb2d804aab280ca5974010064bb 100644 (file)
@@ -7,7 +7,7 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
-use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
+use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, SecretKey};
 use crate::utils::test_logger;
 use core::convert::TryFrom;
 use lightning::offers::invoice_request::UnsignedInvoiceRequest;
@@ -23,7 +23,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
                assert_eq!(data, bytes);
 
                let secp_ctx = Secp256k1::new();
-               let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+               let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
                let pubkey = PublicKey::from(keys);
                let mut buffer = Vec::new();
 
index f2bae246fabeb09f6a2ab827c078b6046c2cd8e5..4c1c5ac1122b286689b97bcf7f0980981d37dc7e 100644 (file)
@@ -1,12 +1,13 @@
 // Imports that need to be added manually
-use bitcoin::bech32::u5;
+use bech32::u5;
 use bitcoin::blockdata::script::ScriptBuf;
 use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, self};
 use bitcoin::secp256k1::ecdh::SharedSecret;
 use bitcoin::secp256k1::ecdsa::RecoverableSignature;
 use bitcoin::secp256k1::schnorr;
 
-use lightning::blinded_path::BlindedPath;
+use lightning::blinded_path::{BlindedPath, EmptyNodeIdLookUp};
+use lightning::blinded_path::message::ForwardNode;
 use lightning::ln::features::InitFeatures;
 use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler};
 use lightning::ln::script::ShutdownScript;
@@ -16,7 +17,7 @@ use lightning::sign::{Recipient, KeyMaterial, EntropySource, NodeSigner, SignerP
 use lightning::util::test_channel_signer::TestChannelSigner;
 use lightning::util::logger::Logger;
 use lightning::util::ser::{Readable, Writeable, Writer};
-use lightning::onion_message::messenger::{CustomOnionMessageHandler, Destination, MessageRouter, OnionMessagePath, OnionMessenger, PendingOnionMessage};
+use lightning::onion_message::messenger::{CustomOnionMessageHandler, Destination, MessageRouter, OnionMessagePath, OnionMessenger, PendingOnionMessage, Responder, ResponseInstruction};
 use lightning::onion_message::offers::{OffersMessage, OffersMessageHandler};
 use lightning::onion_message::packet::OnionMessageContents;
 
@@ -36,12 +37,13 @@ pub fn do_test<L: Logger>(data: &[u8], logger: &L) {
                        node_secret: secret,
                        counter: AtomicU64::new(0),
                };
+               let node_id_lookup = EmptyNodeIdLookUp {};
                let message_router = TestMessageRouter {};
                let offers_msg_handler = TestOffersMessageHandler {};
                let custom_msg_handler = TestCustomMessageHandler {};
                let onion_messenger = OnionMessenger::new(
-                       &keys_manager, &keys_manager, logger, &message_router, &offers_msg_handler,
-                       &custom_msg_handler
+                       &keys_manager, &keys_manager, logger, &node_id_lookup, &message_router,
+                       &offers_msg_handler, &custom_msg_handler
                );
 
                let peer_node_id = {
@@ -87,7 +89,7 @@ impl MessageRouter for TestMessageRouter {
        }
 
        fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
-               &self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
+               &self, _recipient: PublicKey, _peers: Vec<ForwardNode>, _secp_ctx: &Secp256k1<T>,
        ) -> Result<Vec<BlindedPath>, ()> {
                unreachable!()
        }
@@ -96,8 +98,8 @@ impl MessageRouter for TestMessageRouter {
 struct TestOffersMessageHandler {}
 
 impl OffersMessageHandler for TestOffersMessageHandler {
-       fn handle_message(&self, _message: OffersMessage) -> Option<OffersMessage> {
-               None
+       fn handle_message(&self, _message: OffersMessage, _responder: Option<Responder>) -> ResponseInstruction<OffersMessage> {
+               ResponseInstruction::NoResponse
        }
 }
 
@@ -111,6 +113,9 @@ impl OnionMessageContents for TestCustomMessage {
        fn tlv_type(&self) -> u64 {
                CUSTOM_MESSAGE_TYPE
        }
+       fn msg_type(&self) -> &'static str {
+               "Custom Message"
+       }
 }
 
 impl Writeable for TestCustomMessage {
@@ -123,8 +128,11 @@ struct TestCustomMessageHandler {}
 
 impl CustomOnionMessageHandler for TestCustomMessageHandler {
        type CustomMessage = TestCustomMessage;
-       fn handle_custom_message(&self, _msg: Self::CustomMessage) -> Option<Self::CustomMessage> {
-               Some(TestCustomMessage {})
+       fn handle_custom_message(&self, message: Self::CustomMessage, responder: Option<Responder>) -> ResponseInstruction<Self::CustomMessage> {
+               match responder {
+                       Some(responder) => responder.respond(message),
+                       None => ResponseInstruction::NoResponse
+               }
        }
        fn read_custom_message<R: io::Read>(&self, _message_type: u64, buffer: &mut R) -> Result<Option<Self::CustomMessage>, msgs::DecodeError> {
                let mut buf = Vec::new();
@@ -279,9 +287,9 @@ mod tests {
                                                "Received an onion message with path_id None and a reply_path: Custom(TestCustomMessage)"
                                                .to_string())), Some(&1));
                        assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
-                                               "Constructing onion message when responding to Custom onion message with path_id None: TestCustomMessage".to_string())), Some(&1));
+                                               "Constructing onion message when responding with Custom Message to an onion message with path_id None: TestCustomMessage".to_string())), Some(&1));
                        assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
-                                               "Buffered onion message when responding to Custom onion message with path_id None".to_string())), Some(&1));
+                                               "Buffered onion message when responding with Custom Message to an onion message with path_id None".to_string())), Some(&1));
                }
 
                let two_unblinded_hops_om = "\
index 14b136ec35b6d06837c09e997523fc5d093944c1..7937ca1efc60f8ef8e87049b7956acc16c7c1d60 100644 (file)
@@ -7,10 +7,11 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
-use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey, self};
+use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, SecretKey, self};
 use crate::utils::test_logger;
 use core::convert::TryFrom;
 use lightning::blinded_path::BlindedPath;
+use lightning::blinded_path::message::ForwardNode;
 use lightning::sign::EntropySource;
 use lightning::ln::PaymentHash;
 use lightning::ln::features::BlindedHopFeatures;
@@ -27,7 +28,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
                assert_eq!(data, bytes);
 
                let secp_ctx = Secp256k1::new();
-               let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+               let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
                let pubkey = PublicKey::from(keys);
                let mut buffer = Vec::new();
 
@@ -62,9 +63,19 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
        refund: &Refund, signing_pubkey: PublicKey, secp_ctx: &Secp256k1<T>
 ) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
        let entropy_source = Randomness {};
+       let intermediate_nodes = [
+               [
+                       ForwardNode { node_id: pubkey(43), short_channel_id: None },
+                       ForwardNode { node_id: pubkey(44), short_channel_id: None },
+               ],
+               [
+                       ForwardNode { node_id: pubkey(45), short_channel_id: None },
+                       ForwardNode { node_id: pubkey(46), short_channel_id: None },
+               ],
+       ];
        let paths = vec![
-               BlindedPath::new_for_message(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
-               BlindedPath::new_for_message(&[pubkey(45), pubkey(46), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
+               BlindedPath::new_for_message(&intermediate_nodes[0], pubkey(42), &entropy_source, secp_ctx).unwrap(),
+               BlindedPath::new_for_message(&intermediate_nodes[1], pubkey(42), &entropy_source, secp_ctx).unwrap(),
        ];
 
        let payinfo = vec![
index bf94c910531573335f2db2594ce1df41304b0f3d..e801510b0addb12bd821a01b969b1e4310b5ff60 100644 (file)
@@ -7,11 +7,12 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::constants::ChainHash;
 use bitcoin::blockdata::script::Builder;
 use bitcoin::blockdata::transaction::TxOut;
 
-use lightning::blinded_path::{BlindedHop, BlindedPath};
+use lightning::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
 use lightning::chain::transaction::OutPoint;
 use lightning::ln::ChannelId;
 use lightning::ln::channelmanager::{self, ChannelDetails, ChannelCounterparty};
@@ -28,7 +29,7 @@ use lightning::util::ser::Readable;
 
 use bitcoin::hashes::Hash;
 use bitcoin::secp256k1::PublicKey;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 
 use crate::utils::test_logger;
 
@@ -94,8 +95,8 @@ impl<Out: test_logger::Output> UtxoLookup for FuzzChainSource<'_, '_, Out> {
                if input_slice.is_none() { return UtxoResult::Sync(Err(UtxoLookupError::UnknownTx)); }
                let input_slice = input_slice.unwrap();
                let txo_res = TxOut {
-                       value: if input_slice[0] % 2 == 0 { 1_000_000 } else { 1_000 },
-                       script_pubkey: Builder::new().push_int(input_slice[1] as i64).into_script().to_v0_p2wsh(),
+                       value: Amount::from_sat(if input_slice[0] % 2 == 0 { 1_000_000 } else { 1_000 }),
+                       script_pubkey: Builder::new().push_int(input_slice[1] as i64).into_script().to_p2wsh(),
                };
                match input_slice {
                        &[0, _] => UtxoResult::Sync(Err(UtxoLookupError::UnknownChain)),
@@ -157,6 +158,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
                                        msgs::DecodeError::ShortRead => panic!("We picked the length..."),
                                        msgs::DecodeError::Io(e) => panic!("{:?}", e),
                                        msgs::DecodeError::UnsupportedCompression => return,
+                                       msgs::DecodeError::DangerousValue => return,
                                }
                        }
                }}
@@ -362,7 +364,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
                                                });
                                        }
                                        (payinfo, BlindedPath {
-                                               introduction_node_id: hop.src_node_id,
+                                               introduction_node: IntroductionNode::NodeId(hop.src_node_id),
                                                blinding_point: dummy_pk,
                                                blinded_hops,
                                        })
index 89de25aa5e6a192786a36b8d97605dbabf86b25d..a99c397d0b2341c8c4b8b6d76b1f13a345c85540 100644 (file)
@@ -1,6 +1,5 @@
 use lightning::chain;
 use lightning::chain::{chainmonitor, channelmonitor};
-use lightning::chain::chainmonitor::MonitorUpdateId;
 use lightning::chain::transaction::OutPoint;
 use lightning::util::test_channel_signer::TestChannelSigner;
 
@@ -10,11 +9,14 @@ pub struct TestPersister {
        pub update_ret: Mutex<chain::ChannelMonitorUpdateStatus>,
 }
 impl chainmonitor::Persist<TestChannelSigner> for TestPersister {
-       fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor<TestChannelSigner>, _update_id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus {
+       fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor<TestChannelSigner>) -> chain::ChannelMonitorUpdateStatus {
                self.update_ret.lock().unwrap().clone()
        }
 
-       fn update_persisted_channel(&self, _funding_txo: OutPoint, _update: Option<&channelmonitor::ChannelMonitorUpdate>, _data: &channelmonitor::ChannelMonitor<TestChannelSigner>, _update_id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus {
+       fn update_persisted_channel(&self, _funding_txo: OutPoint, _update: Option<&channelmonitor::ChannelMonitorUpdate>, _data: &channelmonitor::ChannelMonitor<TestChannelSigner>) -> chain::ChannelMonitorUpdateStatus {
                self.update_ret.lock().unwrap().clone()
        }
+
+       fn archive_persisted_channel(&self, _: OutPoint) {
+       }
 }
index 841ed55cea337ba114b01f0d7258facb67d3dcba..d8b755c1a7e5a3a7fa9cdb5732b852fd3452404c 100644 (file)
@@ -6,6 +6,7 @@ void full_stack_run(const unsigned char* data, size_t data_len);
 void invoice_deser_run(const unsigned char* data, size_t data_len);
 void invoice_request_deser_run(const unsigned char* data, size_t data_len);
 void offer_deser_run(const unsigned char* data, size_t data_len);
+void bolt11_deser_run(const unsigned char* data, size_t data_len);
 void onion_message_run(const unsigned char* data, size_t data_len);
 void peer_crypt_run(const unsigned char* data, size_t data_len);
 void process_network_graph_run(const unsigned char* data, size_t data_len);
index 1872a1d0714ca73127521e89e073b4fbb0c612f0..e563e2acd9b09a338a6e3ce52e2e1d682726d428 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "lightning-background-processor"
-version = "0.0.121"
+version = "0.0.123-beta"
 authors = ["Valentine Wallace <vwallace@protonmail.com>"]
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/lightningdevkit/rust-lightning"
@@ -21,12 +21,12 @@ no-std = ["bitcoin/no-std", "lightning/no-std", "lightning-rapid-gossip-sync/no-
 default = ["std"]
 
 [dependencies]
-bitcoin = { version = "0.30.2", default-features = false }
-lightning = { version = "0.0.121", path = "../lightning", default-features = false }
-lightning-rapid-gossip-sync = { version = "0.0.121", path = "../lightning-rapid-gossip-sync", default-features = false }
+bitcoin = { version = "0.31.2", default-features = false }
+lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false }
+lightning-rapid-gossip-sync = { version = "0.0.123-beta", path = "../lightning-rapid-gossip-sync", default-features = false }
 
 [dev-dependencies]
 tokio = { version = "1.35", features = [ "macros", "rt", "rt-multi-thread", "sync", "time" ] }
-lightning = { version = "0.0.121", path = "../lightning", features = ["_test_utils"] }
-lightning-invoice = { version = "0.29.0", path = "../lightning-invoice" }
-lightning-persister = { version = "0.0.121", path = "../lightning-persister" }
+lightning = { version = "0.0.123-beta", path = "../lightning", features = ["_test_utils"] }
+lightning-invoice = { version = "0.31.0-beta", path = "../lightning-invoice" }
+lightning-persister = { version = "0.0.123-beta", path = "../lightning-persister" }
index 48581766af8cb97970eeb924297315cc6c93dd2c..4fdad1e1989c2bbceb2237cb9bbf293e634e61f2 100644 (file)
@@ -24,19 +24,17 @@ extern crate lightning_rapid_gossip_sync;
 use lightning::chain;
 use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
 use lightning::chain::chainmonitor::{ChainMonitor, Persist};
-use lightning::sign::{EntropySource, NodeSigner, SignerProvider};
 use lightning::events::{Event, PathFailure};
 #[cfg(feature = "std")]
 use lightning::events::EventHandler;
 #[cfg(any(feature = "std", feature = "futures"))]
 use lightning::events::EventsProvider;
 
-use lightning::ln::channelmanager::ChannelManager;
+use lightning::ln::channelmanager::AChannelManager;
 use lightning::ln::msgs::OnionMessageHandler;
 use lightning::ln::peer_handler::APeerManager;
 use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
 use lightning::routing::utxo::UtxoLookup;
-use lightning::routing::router::Router;
 use lightning::routing::scoring::{ScoreUpdate, WriteableScore};
 use lightning::util::logger::Logger;
 use lightning::util::persist::Persister;
@@ -81,6 +79,8 @@ use alloc::vec::Vec;
 /// However, as long as [`ChannelMonitor`] backups are sound, no funds besides those used for
 /// unilateral chain closure fees are at risk.
 ///
+/// [`ChannelManager`]: lightning::ln::channelmanager::ChannelManager
+/// [`ChannelManager::timer_tick_occurred`]: lightning::ln::channelmanager::ChannelManager::timer_tick_occurred
 /// [`ChannelMonitor`]: lightning::chain::channelmonitor::ChannelMonitor
 /// [`Event`]: lightning::events::Event
 /// [`PeerManager::timer_tick_occurred`]: lightning::ln::peer_handler::PeerManager::timer_tick_occurred
@@ -286,7 +286,7 @@ macro_rules! define_run_body {
                $timer_elapsed: expr, $check_slow_await: expr, $time_fetch: expr,
        ) => { {
                log_trace!($logger, "Calling ChannelManager's timer_tick_occurred on startup");
-               $channel_manager.timer_tick_occurred();
+               $channel_manager.get_cm().timer_tick_occurred();
                log_trace!($logger, "Rebroadcasting monitor's pending claims on startup");
                $chain_monitor.rebroadcast_pending_claims();
 
@@ -336,14 +336,14 @@ macro_rules! define_run_body {
                                break;
                        }
 
-                       if $channel_manager.get_and_clear_needs_persistence() {
+                       if $channel_manager.get_cm().get_and_clear_needs_persistence() {
                                log_trace!($logger, "Persisting ChannelManager...");
-                               $persister.persist_manager(&*$channel_manager)?;
+                               $persister.persist_manager(&$channel_manager)?;
                                log_trace!($logger, "Done persisting ChannelManager.");
                        }
                        if $timer_elapsed(&mut last_freshness_call, FRESHNESS_TIMER) {
                                log_trace!($logger, "Calling ChannelManager's timer_tick_occurred");
-                               $channel_manager.timer_tick_occurred();
+                               $channel_manager.get_cm().timer_tick_occurred();
                                last_freshness_call = $get_timer(FRESHNESS_TIMER);
                        }
                        if $timer_elapsed(&mut last_onion_message_handler_call, ONION_MESSAGE_HANDLER_TIMER) {
@@ -440,7 +440,7 @@ macro_rules! define_run_body {
                // After we exit, ensure we persist the ChannelManager one final time - this avoids
                // some races where users quit while channel updates were in-flight, with
                // ChannelMonitor update(s) persisted without a corresponding ChannelManager update.
-               $persister.persist_manager(&*$channel_manager)?;
+               $persister.persist_manager(&$channel_manager)?;
 
                // Persist Scorer on exit
                if let Some(ref scorer) = $scorer {
@@ -539,45 +539,64 @@ use core::task;
 /// # use std::sync::atomic::{AtomicBool, Ordering};
 /// # use std::time::SystemTime;
 /// # use lightning_background_processor::{process_events_async, GossipSync};
-/// # struct MyStore {}
-/// # impl lightning::util::persist::KVStore for MyStore {
+/// # struct Logger {}
+/// # impl lightning::util::logger::Logger for Logger {
+/// #     fn log(&self, _record: lightning::util::logger::Record) {}
+/// # }
+/// # struct Store {}
+/// # impl lightning::util::persist::KVStore for Store {
 /// #     fn read(&self, primary_namespace: &str, secondary_namespace: &str, key: &str) -> io::Result<Vec<u8>> { Ok(Vec::new()) }
 /// #     fn write(&self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: &[u8]) -> io::Result<()> { Ok(()) }
 /// #     fn remove(&self, primary_namespace: &str, secondary_namespace: &str, key: &str, lazy: bool) -> io::Result<()> { Ok(()) }
 /// #     fn list(&self, primary_namespace: &str, secondary_namespace: &str) -> io::Result<Vec<String>> { Ok(Vec::new()) }
 /// # }
-/// # struct MyEventHandler {}
-/// # impl MyEventHandler {
+/// # struct EventHandler {}
+/// # impl EventHandler {
 /// #     async fn handle_event(&self, _: lightning::events::Event) {}
 /// # }
 /// # #[derive(Eq, PartialEq, Clone, Hash)]
-/// # struct MySocketDescriptor {}
-/// # impl lightning::ln::peer_handler::SocketDescriptor for MySocketDescriptor {
+/// # struct SocketDescriptor {}
+/// # impl lightning::ln::peer_handler::SocketDescriptor for SocketDescriptor {
 /// #     fn send_data(&mut self, _data: &[u8], _resume_read: bool) -> usize { 0 }
 /// #     fn disconnect_socket(&mut self) {}
 /// # }
-/// # type MyBroadcaster = dyn lightning::chain::chaininterface::BroadcasterInterface + Send + Sync;
-/// # type MyFeeEstimator = dyn lightning::chain::chaininterface::FeeEstimator + 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::sign::InMemorySigner, Arc<MyFilter>, Arc<MyBroadcaster>, Arc<MyFeeEstimator>, Arc<MyLogger>, Arc<MyStore>>;
-/// # type MyPeerManager = lightning::ln::peer_handler::SimpleArcPeerManager<MySocketDescriptor, MyChainMonitor, MyBroadcaster, MyFeeEstimator, Arc<MyUtxoLookup>, MyLogger>;
-/// # type MyNetworkGraph = lightning::routing::gossip::NetworkGraph<Arc<MyLogger>>;
-/// # type MyGossipSync = lightning::routing::gossip::P2PGossipSync<Arc<MyNetworkGraph>, Arc<MyUtxoLookup>, Arc<MyLogger>>;
-/// # type MyChannelManager = lightning::ln::channelmanager::SimpleArcChannelManager<MyChainMonitor, MyBroadcaster, MyFeeEstimator, MyLogger>;
-/// # type MyScorer = RwLock<lightning::routing::scoring::ProbabilisticScorer<Arc<MyNetworkGraph>, Arc<MyLogger>>>;
-///
-/// # async fn setup_background_processing(my_persister: Arc<MyStore>, my_event_handler: Arc<MyEventHandler>, my_chain_monitor: Arc<MyChainMonitor>, my_channel_manager: Arc<MyChannelManager>, my_gossip_sync: Arc<MyGossipSync>, my_logger: Arc<MyLogger>, my_scorer: Arc<MyScorer>, my_peer_manager: Arc<MyPeerManager>) {
-///    let background_persister = Arc::clone(&my_persister);
-///    let background_event_handler = Arc::clone(&my_event_handler);
-///    let background_chain_mon = Arc::clone(&my_chain_monitor);
-///    let background_chan_man = Arc::clone(&my_channel_manager);
-///    let background_gossip_sync = GossipSync::p2p(Arc::clone(&my_gossip_sync));
-///    let background_peer_man = Arc::clone(&my_peer_manager);
-///    let background_logger = Arc::clone(&my_logger);
-///    let background_scorer = Arc::clone(&my_scorer);
+/// # type ChainMonitor<B, F, FE> = lightning::chain::chainmonitor::ChainMonitor<lightning::sign::InMemorySigner, Arc<F>, Arc<B>, Arc<FE>, Arc<Logger>, Arc<Store>>;
+/// # type NetworkGraph = lightning::routing::gossip::NetworkGraph<Arc<Logger>>;
+/// # type P2PGossipSync<UL> = lightning::routing::gossip::P2PGossipSync<Arc<NetworkGraph>, Arc<UL>, Arc<Logger>>;
+/// # type ChannelManager<B, F, FE> = lightning::ln::channelmanager::SimpleArcChannelManager<ChainMonitor<B, F, FE>, B, FE, Logger>;
+/// # type Scorer = RwLock<lightning::routing::scoring::ProbabilisticScorer<Arc<NetworkGraph>, Arc<Logger>>>;
+/// # type PeerManager<B, F, FE, UL> = lightning::ln::peer_handler::SimpleArcPeerManager<SocketDescriptor, ChainMonitor<B, F, FE>, B, FE, Arc<UL>, Logger>;
+/// #
+/// # struct Node<
+/// #     B: lightning::chain::chaininterface::BroadcasterInterface + Send + Sync + 'static,
+/// #     F: lightning::chain::Filter + Send + Sync + 'static,
+/// #     FE: lightning::chain::chaininterface::FeeEstimator + Send + Sync + 'static,
+/// #     UL: lightning::routing::utxo::UtxoLookup + Send + Sync + 'static,
+/// # > {
+/// #     peer_manager: Arc<PeerManager<B, F, FE, UL>>,
+/// #     event_handler: Arc<EventHandler>,
+/// #     channel_manager: Arc<ChannelManager<B, F, FE>>,
+/// #     chain_monitor: Arc<ChainMonitor<B, F, FE>>,
+/// #     gossip_sync: Arc<P2PGossipSync<UL>>,
+/// #     persister: Arc<Store>,
+/// #     logger: Arc<Logger>,
+/// #     scorer: Arc<Scorer>,
+/// # }
+/// #
+/// # async fn setup_background_processing<
+/// #     B: lightning::chain::chaininterface::BroadcasterInterface + Send + Sync + 'static,
+/// #     F: lightning::chain::Filter + Send + Sync + 'static,
+/// #     FE: lightning::chain::chaininterface::FeeEstimator + Send + Sync + 'static,
+/// #     UL: lightning::routing::utxo::UtxoLookup + Send + Sync + 'static,
+/// # >(node: Node<B, F, FE, UL>) {
+///    let background_persister = Arc::clone(&node.persister);
+///    let background_event_handler = Arc::clone(&node.event_handler);
+///    let background_chain_mon = Arc::clone(&node.chain_monitor);
+///    let background_chan_man = Arc::clone(&node.channel_manager);
+///    let background_gossip_sync = GossipSync::p2p(Arc::clone(&node.gossip_sync));
+///    let background_peer_man = Arc::clone(&node.peer_manager);
+///    let background_logger = Arc::clone(&node.logger);
+///    let background_scorer = Arc::clone(&node.scorer);
 ///
 ///    // Setup the sleeper.
 ///    let (stop_sender, stop_receiver) = tokio::sync::watch::channel(());
@@ -607,9 +626,9 @@ use core::task;
 ///                    sleeper,
 ///                    mobile_interruptable_platform,
 ///                    || Some(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap())
-///                    )
-///                    .await
-///                    .expect("Failed to process events");
+///            )
+///            .await
+///            .expect("Failed to process events");
 ///    });
 ///
 ///    // Stop the background processing.
@@ -622,21 +641,16 @@ pub async fn process_events_async<
        'a,
        UL: 'static + Deref + Send + Sync,
        CF: 'static + Deref + Send + Sync,
-       CW: 'static + Deref + Send + Sync,
        T: 'static + Deref + Send + Sync,
-       ES: 'static + Deref + Send + Sync,
-       NS: 'static + Deref + Send + Sync,
-       SP: 'static + Deref + Send + Sync,
        F: 'static + Deref + Send + Sync,
-       R: 'static + Deref + Send + Sync,
        G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
        L: 'static + Deref + Send + Sync,
        P: 'static + Deref + Send + Sync,
        EventHandlerFuture: core::future::Future<Output = ()>,
        EventHandler: Fn(Event) -> EventHandlerFuture,
        PS: 'static + Deref + Send,
-       M: 'static + Deref<Target = ChainMonitor<<SP::Target as SignerProvider>::EcdsaSigner, CF, T, F, L, P>> + Send + Sync,
-       CM: 'static + Deref<Target = ChannelManager<CW, T, ES, NS, SP, F, R, L>> + Send + Sync,
+       M: 'static + Deref<Target = ChainMonitor<<CM::Target as AChannelManager>::Signer, CF, T, F, L, P>> + Send + Sync,
+       CM: 'static + Deref + Send + Sync,
        PGS: 'static + Deref<Target = P2PGossipSync<G, UL, L>> + Send + Sync,
        RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
        PM: 'static + Deref + Send + Sync,
@@ -653,16 +667,12 @@ pub async fn process_events_async<
 where
        UL::Target: 'static + UtxoLookup,
        CF::Target: 'static + chain::Filter,
-       CW::Target: 'static + chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
        T::Target: 'static + BroadcasterInterface,
-       ES::Target: 'static + EntropySource,
-       NS::Target: 'static + NodeSigner,
-       SP::Target: 'static + SignerProvider,
        F::Target: 'static + FeeEstimator,
-       R::Target: 'static + Router,
        L::Target: 'static + Logger,
-       P::Target: 'static + Persist<<SP::Target as SignerProvider>::EcdsaSigner>,
-       PS::Target: 'static + Persister<'a, CW, T, ES, NS, SP, F, R, L, SC>,
+       P::Target: 'static + Persist<<CM::Target as AChannelManager>::Signer>,
+       PS::Target: 'static + Persister<'a, CM, L, SC>,
+       CM::Target: AChannelManager + Send + Sync,
        PM::Target: APeerManager + Send + Sync,
 {
        let mut should_break = false;
@@ -693,11 +703,11 @@ where
        define_run_body!(
                persister, chain_monitor,
                chain_monitor.process_pending_events_async(async_event_handler).await,
-               channel_manager, channel_manager.process_pending_events_async(async_event_handler).await,
+               channel_manager, channel_manager.get_cm().process_pending_events_async(async_event_handler).await,
                peer_manager, process_onion_message_handler_events_async(&peer_manager, async_event_handler).await,
                gossip_sync, logger, scorer, should_break, {
                        let fut = Selector {
-                               a: channel_manager.get_event_or_persistence_needed_future(),
+                               a: channel_manager.get_cm().get_event_or_persistence_needed_future(),
                                b: chain_monitor.get_update_future(),
                                c: sleeper(if mobile_interruptable_platform { Duration::from_millis(100) } else { Duration::from_secs(FASTEST_TIMER) }),
                        };
@@ -788,20 +798,15 @@ impl BackgroundProcessor {
                'a,
                UL: 'static + Deref + Send + Sync,
                CF: 'static + Deref + Send + Sync,
-               CW: 'static + Deref + Send + Sync,
                T: 'static + Deref + Send + Sync,
-               ES: 'static + Deref + Send + Sync,
-               NS: 'static + Deref + Send + Sync,
-               SP: 'static + Deref + Send + Sync,
                F: 'static + Deref + Send + Sync,
-               R: 'static + Deref + Send + Sync,
                G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
                L: 'static + Deref + Send + Sync,
                P: 'static + Deref + Send + Sync,
                EH: 'static + EventHandler + Send,
                PS: 'static + Deref + Send,
-               M: 'static + Deref<Target = ChainMonitor<<SP::Target as SignerProvider>::EcdsaSigner, CF, T, F, L, P>> + Send + Sync,
-               CM: 'static + Deref<Target = ChannelManager<CW, T, ES, NS, SP, F, R, L>> + Send + Sync,
+               M: 'static + Deref<Target = ChainMonitor<<CM::Target as AChannelManager>::Signer, CF, T, F, L, P>> + Send + Sync,
+               CM: 'static + Deref + Send + Sync,
                PGS: 'static + Deref<Target = P2PGossipSync<G, UL, L>> + Send + Sync,
                RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
                PM: 'static + Deref + Send + Sync,
@@ -814,16 +819,12 @@ impl BackgroundProcessor {
        where
                UL::Target: 'static + UtxoLookup,
                CF::Target: 'static + chain::Filter,
-               CW::Target: 'static + chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
                T::Target: 'static + BroadcasterInterface,
-               ES::Target: 'static + EntropySource,
-               NS::Target: 'static + NodeSigner,
-               SP::Target: 'static + SignerProvider,
                F::Target: 'static + FeeEstimator,
-               R::Target: 'static + Router,
                L::Target: 'static + Logger,
-               P::Target: 'static + Persist<<SP::Target as SignerProvider>::EcdsaSigner>,
-               PS::Target: 'static + Persister<'a, CW, T, ES, NS, SP, F, R, L, SC>,
+               P::Target: 'static + Persist<<CM::Target as AChannelManager>::Signer>,
+               PS::Target: 'static + Persister<'a, CM, L, SC>,
+               CM::Target: AChannelManager + Send + Sync,
                PM::Target: APeerManager + Send + Sync,
        {
                let stop_thread = Arc::new(AtomicBool::new(false));
@@ -849,12 +850,12 @@ impl BackgroundProcessor {
                        };
                        define_run_body!(
                                persister, chain_monitor, chain_monitor.process_pending_events(&event_handler),
-                               channel_manager, channel_manager.process_pending_events(&event_handler),
+                               channel_manager, channel_manager.get_cm().process_pending_events(&event_handler),
                                peer_manager,
                                peer_manager.onion_message_handler().process_pending_events(&event_handler),
                                gossip_sync, logger, scorer, stop_thread.load(Ordering::Acquire),
                                { Sleeper::from_two_futures(
-                                       &channel_manager.get_event_or_persistence_needed_future(),
+                                       &channel_manager.get_cm().get_event_or_persistence_needed_future(),
                                        &chain_monitor.get_update_future()
                                ).wait_timeout(Duration::from_millis(100)); },
                                |_| Instant::now(), |time: &Instant, dur| time.elapsed().as_secs() > dur, false,
@@ -918,18 +919,21 @@ impl Drop for BackgroundProcessor {
 
 #[cfg(all(feature = "std", test))]
 mod tests {
+       use bitcoin::{Amount, ScriptBuf, Txid};
        use bitcoin::blockdata::constants::{genesis_block, ChainHash};
        use bitcoin::blockdata::locktime::absolute::LockTime;
        use bitcoin::blockdata::transaction::{Transaction, TxOut};
-       use bitcoin::network::constants::Network;
+       use bitcoin::hashes::Hash;
+       use bitcoin::network::Network;
        use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1};
-       use lightning::chain::{BestBlock, Confirm, chainmonitor};
+       use bitcoin::transaction::Version;
+       use lightning::chain::{BestBlock, Confirm, chainmonitor, Filter};
        use lightning::chain::channelmonitor::ANTI_REORG_DELAY;
-       use lightning::sign::{InMemorySigner, KeysManager};
+       use lightning::sign::{InMemorySigner, KeysManager, ChangeDestinationSource};
        use lightning::chain::transaction::OutPoint;
        use lightning::events::{Event, PathFailure, MessageSendEventsProvider, MessageSendEvent};
        use lightning::{get_event_msg, get_event};
-       use lightning::ln::{PaymentHash, ChannelId};
+       use lightning::ln::types::{PaymentHash, ChannelId};
        use lightning::ln::channelmanager;
        use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, MIN_CLTV_EXPIRY_DELTA, PaymentId};
        use lightning::ln::features::{ChannelFeatures, NodeFeatures};
@@ -946,6 +950,7 @@ mod tests {
                CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, CHANNEL_MANAGER_PERSISTENCE_KEY,
                NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE, NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE, NETWORK_GRAPH_PERSISTENCE_KEY,
                SCORER_PERSISTENCE_PRIMARY_NAMESPACE, SCORER_PERSISTENCE_SECONDARY_NAMESPACE, SCORER_PERSISTENCE_KEY};
+       use lightning::util::sweep::{OutputSweeper, OutputSpendStatus};
        use lightning_persister::fs_store::FilesystemStore;
        use std::collections::VecDeque;
        use std::{fs, env};
@@ -1008,6 +1013,9 @@ mod tests {
                logger: Arc<test_utils::TestLogger>,
                best_block: BestBlock,
                scorer: Arc<LockingWrapper<TestScorer>>,
+               sweeper: Arc<OutputSweeper<Arc<test_utils::TestBroadcaster>, Arc<TestWallet>,
+                       Arc<test_utils::TestFeeEstimator>, Arc<dyn Filter + Sync + Send>, Arc<FilesystemStore>,
+                       Arc<test_utils::TestLogger>, Arc<KeysManager>>>,
        }
 
        impl Node {
@@ -1246,6 +1254,14 @@ mod tests {
                }
        }
 
+       struct TestWallet {}
+
+       impl ChangeDestinationSource for TestWallet {
+               fn get_change_destination_script(&self) -> Result<ScriptBuf, ()> {
+                       Ok(ScriptBuf::new())
+               }
+       }
+
        fn get_full_filepath(filepath: String, filename: String) -> String {
                let mut path = PathBuf::from(filepath);
                path.push(filename);
@@ -1270,10 +1286,15 @@ mod tests {
                        let router = Arc::new(DefaultRouter::new(network_graph.clone(), logger.clone(), Arc::clone(&keys_manager), scorer.clone(), Default::default()));
                        let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Bitcoin));
                        let kv_store = Arc::new(FilesystemStore::new(format!("{}_persister_{}", &persist_dir, i).into()));
+                       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(), kv_store.clone()));
                        let best_block = BestBlock::from_network(network);
                        let params = ChainParameters { network, best_block };
                        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, genesis_block.header.time));
+                       let wallet = Arc::new(TestWallet {});
+                       let sweeper = Arc::new(OutputSweeper::new(best_block, Arc::clone(&tx_broadcaster), Arc::clone(&fee_estimator),
+                               None::<Arc<dyn Filter + Sync + Send>>, Arc::clone(&keys_manager), wallet, Arc::clone(&kv_store), Arc::clone(&logger)));
                        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 {
@@ -1282,7 +1303,7 @@ mod tests {
                                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, kv_store, tx_broadcaster, network_graph, logger, best_block, scorer };
+                       let node = Node { node: manager, p2p_gossip_sync, rapid_gossip_sync, peer_manager, chain_monitor, kv_store, tx_broadcaster, network_graph, logger, best_block, scorer, sweeper };
                        nodes.push(node);
                }
 
@@ -1330,8 +1351,8 @@ mod tests {
                                        assert_eq!(channel_value_satoshis, $channel_value);
                                        assert_eq!(user_channel_id, 42);
 
-                                       let tx = Transaction { version: 1 as i32, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
-                                               value: channel_value_satoshis, script_pubkey: output_script.clone(),
+                                       let tx = Transaction { version: Version::ONE, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
+                                               value: Amount::from_sat(channel_value_satoshis), script_pubkey: output_script.clone(),
                                        }]};
                                        (temporary_channel_id, tx)
                                },
@@ -1351,15 +1372,40 @@ mod tests {
                                1 => {
                                        node.node.transactions_confirmed(&header, &txdata, height);
                                        node.chain_monitor.transactions_confirmed(&header, &txdata, height);
+                                       node.sweeper.transactions_confirmed(&header, &txdata, height);
                                },
                                x if x == depth => {
+                                       // We need the TestBroadcaster to know about the new height so that it doesn't think
+                                       // we're violating the time lock requirements of transactions broadcasted at that
+                                       // point.
+                                       node.tx_broadcaster.blocks.lock().unwrap().push((genesis_block(Network::Bitcoin), height));
                                        node.node.best_block_updated(&header, height);
                                        node.chain_monitor.best_block_updated(&header, height);
+                                       node.sweeper.best_block_updated(&header, height);
                                },
                                _ => {},
                        }
                }
        }
+
+       fn advance_chain(node: &mut Node, num_blocks: u32) {
+               for i in 1..=num_blocks {
+                       let prev_blockhash = node.best_block.block_hash;
+                       let height = node.best_block.height + 1;
+                       let header = create_dummy_header(prev_blockhash, height);
+                       node.best_block = BestBlock::new(header.block_hash(), height);
+                       if i == num_blocks {
+                               // We need the TestBroadcaster to know about the new height so that it doesn't think
+                               // we're violating the time lock requirements of transactions broadcasted at that
+                               // point.
+                               node.tx_broadcaster.blocks.lock().unwrap().push((genesis_block(Network::Bitcoin), height));
+                               node.node.best_block_updated(&header, height);
+                               node.chain_monitor.best_block_updated(&header, height);
+                               node.sweeper.best_block_updated(&header, height);
+                       }
+               }
+       }
+
        fn confirm_transaction(node: &mut Node, tx: &Transaction) {
                confirm_transaction_depth(node, tx, ANTI_REORG_DELAY);
        }
@@ -1591,6 +1637,9 @@ mod tests {
                let _as_channel_update = get_event_msg!(nodes[0], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id());
                nodes[1].node.handle_channel_ready(&nodes[0].node.get_our_node_id(), &as_funding);
                let _bs_channel_update = get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, nodes[0].node.get_our_node_id());
+               let broadcast_funding = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().pop().unwrap();
+               assert_eq!(broadcast_funding.txid(), funding_tx.txid());
+               assert!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().is_empty());
 
                if !std::thread::panicking() {
                        bg_processor.stop().unwrap();
@@ -1616,10 +1665,95 @@ mod tests {
                        .recv_timeout(Duration::from_secs(EVENT_DEADLINE))
                        .expect("Events not handled within deadline");
                match event {
-                       Event::SpendableOutputs { .. } => {},
+                       Event::SpendableOutputs { outputs, channel_id } => {
+                               nodes[0].sweeper.track_spendable_outputs(outputs, channel_id, false, Some(153)).unwrap();
+                       },
                        _ => panic!("Unexpected event: {:?}", event),
                }
 
+               // Check we don't generate an initial sweeping tx until we reach the required height.
+               assert_eq!(nodes[0].sweeper.tracked_spendable_outputs().len(), 1);
+               let tracked_output = nodes[0].sweeper.tracked_spendable_outputs().first().unwrap().clone();
+               if let Some(sweep_tx_0) = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().pop() {
+                       assert!(!tracked_output.is_spent_in(&sweep_tx_0));
+                       match tracked_output.status {
+                               OutputSpendStatus::PendingInitialBroadcast { delayed_until_height } => {
+                                       assert_eq!(delayed_until_height, Some(153));
+                               }
+                               _ => panic!("Unexpected status"),
+                       }
+               }
+
+               advance_chain(&mut nodes[0], 3);
+
+               // Check we generate an initial sweeping tx.
+               assert_eq!(nodes[0].sweeper.tracked_spendable_outputs().len(), 1);
+               let tracked_output = nodes[0].sweeper.tracked_spendable_outputs().first().unwrap().clone();
+               let sweep_tx_0 = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().pop().unwrap();
+               match tracked_output.status {
+                       OutputSpendStatus::PendingFirstConfirmation { latest_spending_tx, .. } => {
+                               assert_eq!(sweep_tx_0.txid(), latest_spending_tx.txid());
+                       }
+                       _ => panic!("Unexpected status"),
+               }
+
+               // Check we regenerate and rebroadcast the sweeping tx each block.
+               advance_chain(&mut nodes[0], 1);
+               assert_eq!(nodes[0].sweeper.tracked_spendable_outputs().len(), 1);
+               let tracked_output = nodes[0].sweeper.tracked_spendable_outputs().first().unwrap().clone();
+               let sweep_tx_1 = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().pop().unwrap();
+               match tracked_output.status {
+                       OutputSpendStatus::PendingFirstConfirmation { latest_spending_tx, .. } => {
+                               assert_eq!(sweep_tx_1.txid(), latest_spending_tx.txid());
+                       }
+                       _ => panic!("Unexpected status"),
+               }
+               assert_ne!(sweep_tx_0, sweep_tx_1);
+
+               advance_chain(&mut nodes[0], 1);
+               assert_eq!(nodes[0].sweeper.tracked_spendable_outputs().len(), 1);
+               let tracked_output = nodes[0].sweeper.tracked_spendable_outputs().first().unwrap().clone();
+               let sweep_tx_2 = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().pop().unwrap();
+               match tracked_output.status {
+                       OutputSpendStatus::PendingFirstConfirmation { latest_spending_tx, .. } => {
+                               assert_eq!(sweep_tx_2.txid(), latest_spending_tx.txid());
+                       }
+                       _ => panic!("Unexpected status"),
+               }
+               assert_ne!(sweep_tx_0, sweep_tx_2);
+               assert_ne!(sweep_tx_1, sweep_tx_2);
+
+               // Check we still track the spendable outputs up to ANTI_REORG_DELAY confirmations.
+               confirm_transaction_depth(&mut nodes[0], &sweep_tx_2, 5);
+               assert_eq!(nodes[0].sweeper.tracked_spendable_outputs().len(), 1);
+               let tracked_output = nodes[0].sweeper.tracked_spendable_outputs().first().unwrap().clone();
+               match tracked_output.status {
+                       OutputSpendStatus::PendingThresholdConfirmations { latest_spending_tx, .. } => {
+                               assert_eq!(sweep_tx_2.txid(), latest_spending_tx.txid());
+                       }
+                       _ => panic!("Unexpected status"),
+               }
+
+               // Check we still see the transaction as confirmed if we unconfirm any untracked
+               // transaction. (We previously had a bug that would mark tracked transactions as
+               // unconfirmed if any transaction at an unknown block height would be unconfirmed.)
+               let unconf_txid = Txid::from_slice(&[0; 32]).unwrap();
+               nodes[0].sweeper.transaction_unconfirmed(&unconf_txid);
+
+               assert_eq!(nodes[0].sweeper.tracked_spendable_outputs().len(), 1);
+               let tracked_output = nodes[0].sweeper.tracked_spendable_outputs().first().unwrap().clone();
+               match tracked_output.status {
+                       OutputSpendStatus::PendingThresholdConfirmations { latest_spending_tx, .. } => {
+                               assert_eq!(sweep_tx_2.txid(), latest_spending_tx.txid());
+                       }
+                       _ => panic!("Unexpected status"),
+               }
+
+               // Check we stop tracking the spendable outputs when one of the txs reaches
+               // ANTI_REORG_DELAY confirmations.
+               confirm_transaction_depth(&mut nodes[0], &sweep_tx_0, ANTI_REORG_DELAY);
+               assert_eq!(nodes[0].sweeper.tracked_spendable_outputs().len(), 0);
+
                if !std::thread::panicking() {
                        bg_processor.stop().unwrap();
                }
index 5e41f4d19aea11825462a355f28261111fac07c3..b232a6ae4abbfd7bd740893ae3dcdbc021634e3e 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "lightning-block-sync"
-version = "0.0.121"
+version = "0.0.123-beta"
 authors = ["Jeffrey Czyz", "Matt Corallo"]
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/lightningdevkit/rust-lightning"
@@ -18,13 +18,13 @@ rest-client = [ "serde_json", "chunked_transfer" ]
 rpc-client = [ "serde_json", "chunked_transfer" ]
 
 [dependencies]
-bitcoin = "0.30.2"
+bitcoin = "0.31.2"
 hex = { package = "hex-conservative", version = "0.1.1", default-features = false }
-lightning = { version = "0.0.121", path = "../lightning" }
+lightning = { version = "0.0.123-beta", path = "../lightning" }
 tokio = { version = "1.35", features = [ "io-util", "net", "time", "rt" ], optional = true }
 serde_json = { version = "1.0", optional = true }
 chunked_transfer = { version = "1.4", optional = true }
 
 [dev-dependencies]
-lightning = { version = "0.0.121", path = "../lightning", features = ["_test_utils"] }
+lightning = { version = "0.0.123-beta", path = "../lightning", features = ["_test_utils"] }
 tokio = { version = "1.35", features = [ "macros", "rt" ] }
index ed811d2cc0c3f629e16a450937c58d8287f35689..e3bf962205043b543a39ea3dab78888659e2622a 100644 (file)
@@ -277,7 +277,7 @@ pub(crate) mod tests {
        use super::*;
        use bitcoin::blockdata::constants::genesis_block;
        use bitcoin::hashes::Hash;
-       use bitcoin::network::constants::Network;
+       use bitcoin::network::Network;
        use hex::DisplayHex;
        use serde_json::value::Number;
        use serde_json::Value;
index 8cb0ff70a2e67ee1dee3330ffa77532b8bd68b83..38c21f2f9cb8007e550a6febb43fe23fa0d491ce 100644 (file)
@@ -6,7 +6,7 @@ use crate::poll::{ChainPoller, Validate, ValidatedBlockHeader};
 
 use bitcoin::blockdata::block::Header;
 use bitcoin::hash_types::BlockHash;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 
 use lightning::chain;
 
@@ -38,7 +38,7 @@ BlockSourceResult<ValidatedBlockHeader> where B::Target: BlockSource {
 ///
 /// ```
 /// use bitcoin::hash_types::BlockHash;
-/// use bitcoin::network::constants::Network;
+/// use bitcoin::network::Network;
 ///
 /// use lightning::chain;
 /// use lightning::chain::Watch;
@@ -252,8 +252,6 @@ mod tests {
        use crate::test_utils::{Blockchain, MockChainListener};
        use super::*;
 
-       use bitcoin::network::constants::Network;
-
        #[tokio::test]
        async fn sync_from_same_chain() {
                let chain = Blockchain::default().with_height(4);
index 4a01d4673b31e91d56c3cb350d995c1b7a3d7403..c54e054454876799cfa2a862f23d2a3be10a0f74 100644 (file)
@@ -439,7 +439,7 @@ mod spv_client_tests {
        use crate::test_utils::{Blockchain, NullChainListener};
        use super::*;
 
-       use bitcoin::network::constants::Network;
+       use bitcoin::network::Network;
 
        #[tokio::test]
        async fn poll_from_chain_without_headers() {
@@ -566,7 +566,7 @@ mod chain_notifier_tests {
        use crate::test_utils::{Blockchain, MockChainListener};
        use super::*;
 
-       use bitcoin::network::constants::Network;
+       use bitcoin::network::Network;
 
        #[tokio::test]
        async fn sync_from_same_chain() {
index dcc19a4969dd3eab863c1216b17d434b93f54849..7f0f74ce5ce7b57ed66ad9b3dc427da0b41825f6 100644 (file)
@@ -3,7 +3,7 @@
 use crate::{AsyncBlockSourceResult, BlockData, BlockHeaderData, BlockSource, BlockSourceError, BlockSourceResult};
 
 use bitcoin::hash_types::BlockHash;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 use lightning::chain::BestBlock;
 
 use std::ops::Deref;
index b6fa6617c846e2d57f5288b0a6ecab975425081a..84c65f244f00e2c2d3fb81ebf976e535c06f8ac7 100644 (file)
@@ -5,8 +5,9 @@ use bitcoin::blockdata::block::{Block, Header, Version};
 use bitcoin::blockdata::constants::genesis_block;
 use bitcoin::blockdata::locktime::absolute::LockTime;
 use bitcoin::hash_types::{BlockHash, TxMerkleNode};
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 use bitcoin::Transaction;
+use bitcoin::transaction;
 
 use lightning::chain;
 
@@ -44,7 +45,7 @@ impl Blockchain {
                        // Note that elsewhere in tests we assume that the merkle root of an empty block is all zeros,
                        // but that's OK because those tests don't trigger the check.
                        let coinbase = Transaction {
-                               version: 0,
+                               version: transaction::Version(0),
                                lock_time: LockTime::ZERO,
                                input: vec![],
                                output: vec![]
index b841f5f9d413d90dfa45ebe8cf18516490a9092e..98a720a2eb2778a48224bf91b963e35b72c2954c 100644 (file)
@@ -1,7 +1,7 @@
-use bitcoin::hashes::hex::FromHex;
+use bitcoin::hashes::hex::{FromHex, HexToArrayError};
 use bitcoin::pow::Work;
 
-pub fn hex_to_work(hex: &str) -> Result<Work, bitcoin::hashes::hex::Error> {
+pub fn hex_to_work(hex: &str) -> Result<Work, HexToArrayError> {
        let bytes = <[u8; 32]>::from_hex(hex)?;
        Ok(Work::from_be_bytes(bytes))
 }
@@ -9,6 +9,7 @@ pub fn hex_to_work(hex: &str) -> Result<Work, bitcoin::hashes::hex::Error> {
 #[cfg(test)]
 mod tests {
        use super::*;
+       use bitcoin::hashes::hex::HexToBytesError;
        use bitcoin::pow::Work;
 
        #[test]
@@ -19,25 +20,25 @@ mod tests {
        #[test]
        fn hex_to_work_too_short_str() {
                let hex = String::from_utf8(vec![b'0'; 32]).unwrap();
-               assert_eq!(hex_to_work(&hex), Err(bitcoin::hashes::hex::Error::InvalidLength(64, 32)));
+               assert_eq!(hex_to_work(&hex), Err(HexToArrayError::InvalidLength(32, 64)));
        }
 
        #[test]
        fn hex_to_work_too_long_str() {
                let hex = String::from_utf8(vec![b'0'; 128]).unwrap();
-               assert_eq!(hex_to_work(&hex), Err(bitcoin::hashes::hex::Error::InvalidLength(64, 128)));
+               assert_eq!(hex_to_work(&hex), Err(HexToArrayError::InvalidLength(128, 64)));
        }
 
        #[test]
        fn hex_to_work_odd_length_str() {
                let hex = String::from_utf8(vec![b'0'; 65]).unwrap();
-               assert_eq!(hex_to_work(&hex), Err(bitcoin::hashes::hex::Error::OddLengthString(65)));
+               assert_eq!(hex_to_work(&hex), Err(HexToArrayError::Conversion(HexToBytesError::OddLengthString(65))));
        }
 
        #[test]
        fn hex_to_work_invalid_char() {
                let hex = String::from_utf8(vec![b'G'; 64]).unwrap();
-               assert_eq!(hex_to_work(&hex), Err(bitcoin::hashes::hex::Error::InvalidChar(b'G')));
+               assert_eq!(hex_to_work(&hex), Err(HexToArrayError::Conversion(HexToBytesError::InvalidChar(b'G'))));
        }
 
        #[test]
index fbec38431c42acc209e1756bbc7b1c1dc8bbc964..ce358159596172f69c3ce06a3ce5035395989de1 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "lightning-custom-message"
-version = "0.0.121"
+version = "0.0.123-beta"
 authors = ["Jeffrey Czyz"]
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/lightningdevkit/rust-lightning"
@@ -14,5 +14,5 @@ all-features = true
 rustdoc-args = ["--cfg", "docsrs"]
 
 [dependencies]
-bitcoin = "0.30.2"
-lightning = { version = "0.0.121", path = "../lightning" }
+bitcoin = "0.31.2"
+lightning = { version = "0.0.123-beta", path = "../lightning" }
index a0a70c9de03ff26dfd499bb7b8cb1dc6eeee8dbe..e2000bc5804c35023c669a94240c4406619f44ef 100644 (file)
@@ -12,6 +12,7 @@
 //! `Foo` and `Bar` messages, and further composing it with a handler for `Baz` messages.
 //!
 //!```
+//! # fn main() {} // Avoid #[macro_export] generating an in-function warning
 //! # extern crate bitcoin;
 //! extern crate lightning;
 //! #[macro_use]
 //! #     }
 //! }
 //!
-//! # fn main() {
 //! // The first crate may define a handler composing `FooHandler` and `BarHandler` and export the
 //! // corresponding message type ids as a macro to use in further composition.
 //!
 //! macro_rules! foo_bar_baz_type_ids {
 //!     () => { foo_bar_type_ids!() | baz_type_id!() }
 //! }
-//! # }
 //!```
 //!
 //! [BOLT 1]: https://github.com/lightning/bolts/blob/master/01-messaging.md
index 1d2a4cdbfbe4ae2d0e3d106e541a3688b5c35ac5..22f8616a2f9731ab5be7c3c110870fe1e96cabb6 100644 (file)
@@ -1,7 +1,7 @@
 [package]
 name = "lightning-invoice"
 description = "Data structures to parse and serialize BOLT11 lightning invoices"
-version = "0.29.0"
+version = "0.31.0-beta"
 authors = ["Sebastian Geisler <sgeisler@wh2.tu-dresden.de>"]
 documentation = "https://docs.rs/lightning-invoice/"
 license = "MIT OR Apache-2.0"
@@ -17,18 +17,17 @@ rustdoc-args = ["--cfg", "docsrs"]
 [features]
 default = ["std"]
 no-std = ["lightning/no-std"]
-std = ["bitcoin/std", "num-traits/std", "lightning/std", "bech32/std"]
+std = ["bitcoin/std", "lightning/std", "bech32/std"]
 
 [dependencies]
-bech32 = { version = "0.9.0", default-features = false }
-lightning = { version = "0.0.121", path = "../lightning", default-features = false }
-secp256k1 = { version = "0.27.0", default-features = false, features = ["recovery", "alloc"] }
-num-traits = { version = "0.2.8", default-features = false }
+bech32 = { version = "0.9.1", default-features = false }
+lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false }
+secp256k1 = { version = "0.28.0", default-features = false, features = ["recovery", "alloc"] }
 serde = { version = "1.0.118", optional = true }
-bitcoin = { version = "0.30.2", default-features = false }
+bitcoin = { version = "0.31.2", default-features = false }
 
 [dev-dependencies]
-lightning = { version = "0.0.121", path = "../lightning", default-features = false, features = ["_test_utils"] }
+lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false, features = ["_test_utils"] }
 hex = { package = "hex-conservative", version = "0.1.1", default-features = false }
 serde_json = { version = "1"}
 hashbrown = { version = "0.13", default-features = false }
diff --git a/lightning-invoice/fuzz/.gitignore b/lightning-invoice/fuzz/.gitignore
deleted file mode 100644 (file)
index 38a9008..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-target
-hfuzz_*
diff --git a/lightning-invoice/fuzz/Cargo.toml b/lightning-invoice/fuzz/Cargo.toml
deleted file mode 100644 (file)
index 746fe63..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-[package]
-name = "lightning-invoice-fuzz"
-version = "0.0.1"
-authors = ["Automatically generated"]
-publish = false
-edition = "2021"
-
-[package.metadata]
-cargo-fuzz = true
-
-[features]
-afl_fuzz = ["afl"]
-honggfuzz_fuzz = ["honggfuzz"]
-
-[dependencies]
-honggfuzz = { version = "0.5", optional = true, default-features = false }
-afl = { version = "0.4", optional = true }
-lightning-invoice = { path = ".." }
-lightning = { path = "../../lightning", features = ["regex"] }
-bech32 = "0.9.0"
-
-# Prevent this from interfering with workspaces
-[workspace]
-members = ["."]
-
-[[bin]]
-name = "serde_data_part"
-path = "fuzz_targets/serde_data_part.rs"
diff --git a/lightning-invoice/fuzz/ci-fuzz.sh b/lightning-invoice/fuzz/ci-fuzz.sh
deleted file mode 100755 (executable)
index db1b9eb..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-set -e
-cargo install --force honggfuzz --no-default-features
-for TARGET in fuzz_targets/*; do
-    FILENAME=$(basename $TARGET)
-       FILE="${FILENAME%.*}"
-       if [ -d hfuzz_input/$FILE ]; then
-           HFUZZ_INPUT_ARGS="-f hfuzz_input/$FILE/input"
-       fi
-       HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz" HFUZZ_RUN_ARGS="-N1000000 --exit_upon_crash -v $HFUZZ_INPUT_ARGS" cargo hfuzz run $FILE
-
-       if [ -f hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT ]; then
-               cat hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT
-               for CASE in hfuzz_workspace/$FILE/SIG*; do
-                       cat $CASE | xxd -p
-               done
-               exit 1
-       fi
-done
diff --git a/lightning-invoice/fuzz/fuzz_targets/serde_data_part.rs b/lightning-invoice/fuzz/fuzz_targets/serde_data_part.rs
deleted file mode 100644 (file)
index 871c6c7..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-extern crate lightning_invoice;
-extern crate bech32;
-
-use lightning_invoice::RawDataPart;
-use bech32::{FromBase32, ToBase32, u5};
-
-fn do_test(data: &[u8]) {
-    let bech32 = data.iter().map(|x| u5::try_from_u8(x % 32).unwrap()).collect::<Vec<_>>();
-    let invoice = match RawDataPart::from_base32(&bech32) {
-        Ok(invoice) => invoice,
-        Err(_) => return,
-    };
-
-    // Our encoding is not worse than the input
-    assert!(invoice.to_base32().len() <= bech32.len());
-
-    // Our serialization is loss-less
-    assert_eq!(
-        RawDataPart::from_base32(&invoice.to_base32()).expect("faild parsing out own encoding"),
-        invoice
-    );
-}
-
-#[cfg(feature = "afl")]
-#[macro_use] extern crate afl;
-#[cfg(feature = "afl")]
-fn main() {
-    fuzz!(|data| {
-        do_test(&data);
-    });
-}
-
-#[cfg(feature = "honggfuzz")]
-#[macro_use] extern crate honggfuzz;
-#[cfg(feature = "honggfuzz")]
-fn main() {
-    loop {
-        fuzz!(|data| {
-            do_test(data);
-        });
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
-        let mut b = 0;
-        for (idx, c) in hex.as_bytes().iter().filter(|&&c| c != b'\n').enumerate() {
-            b <<= 4;
-            match *c {
-                b'A'..=b'F' => b |= c - b'A' + 10,
-                b'a'..=b'f' => b |= c - b'a' + 10,
-                b'0'..=b'9' => b |= c - b'0',
-                _ => panic!("Bad hex"),
-            }
-            if (idx & 1) == 1 {
-                out.push(b);
-                b = 0;
-            }
-        }
-    }
-
-    #[test]
-    fn duplicate_crash() {
-        let mut a = Vec::new();
-        extend_vec_from_hex("000000", &mut a);
-        super::do_test(&a);
-    }
-}
index c75373ea9dac3d127575b95d78954400164d1dc9..bd9f4a5f6dedcf2b15502f821a6951a92ec5e99f 100644 (file)
@@ -1,5 +1,6 @@
 #[cfg(feature = "std")]
 use std::error;
+#[cfg(not(feature = "std"))]
 use core::convert::TryFrom;
 use core::fmt;
 use core::fmt::{Display, Formatter};
@@ -9,17 +10,14 @@ use core::str::FromStr;
 
 use bech32::{u5, FromBase32};
 
-use bitcoin::{PubkeyHash, ScriptHash};
-use bitcoin::address::WitnessVersion;
+use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
 use bitcoin::hashes::Hash;
 use bitcoin::hashes::sha256;
 use crate::prelude::*;
-use lightning::ln::PaymentSecret;
+use lightning::ln::types::PaymentSecret;
 use lightning::routing::gossip::RoutingFees;
 use lightning::routing::router::{RouteHint, RouteHintHop};
 
-use num_traits::{CheckedAdd, CheckedMul};
-
 use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
 use secp256k1::PublicKey;
 
@@ -44,7 +42,11 @@ mod hrp_sm {
        }
 
        impl States {
-               fn next_state(&self, read_symbol: char) -> Result<States, super::Bolt11ParseError> {
+               fn next_state(&self, read_byte: u8) -> Result<States, super::Bolt11ParseError> {
+                       let read_symbol = match char::from_u32(read_byte.into()) {
+                               Some(symb) if symb.is_ascii() => symb,
+                               _ => return Err(super::Bolt11ParseError::MalformedHRP),
+                       };
                        match *self {
                                States::Start => {
                                        if read_symbol == 'l' {
@@ -120,7 +122,7 @@ mod hrp_sm {
                        *range = Some(new_range);
                }
 
-               fn step(&mut self, c: char) -> Result<(), super::Bolt11ParseError> {
+               fn step(&mut self, c: u8) -> Result<(), super::Bolt11ParseError> {
                        let next_state = self.state.next_state(c)?;
                        match next_state {
                                States::ParseCurrencyPrefix => {
@@ -159,7 +161,7 @@ mod hrp_sm {
 
        pub fn parse_hrp(input: &str) -> Result<(&str, &str, &str), super::Bolt11ParseError> {
                let mut sm = StateMachine::new();
-               for c in input.chars() {
+               for c in input.bytes() {
                        sm.step(c)?;
                }
 
@@ -356,7 +358,7 @@ impl FromBase32 for PositiveTimestamp {
                if b32.len() != 7 {
                        return Err(Bolt11ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
                }
-               let timestamp: u64 = parse_int_be(b32, 32)
+               let timestamp: u64 = parse_u64_be(b32)
                        .expect("7*5bit < 64bit, no overflow possible");
                match PositiveTimestamp::from_unix_timestamp(timestamp) {
                        Ok(t) => Ok(t),
@@ -382,16 +384,17 @@ impl FromBase32 for Bolt11InvoiceSignature {
        }
 }
 
-pub(crate) fn parse_int_be<T, U>(digits: &[U], base: T) -> Option<T>
-       where T: CheckedAdd + CheckedMul + From<u8> + Default,
-             U: Into<u8> + Copy
-{
-       digits.iter().fold(Some(Default::default()), |acc, b|
-               acc
-                       .and_then(|x| x.checked_mul(&base))
-                       .and_then(|x| x.checked_add(&(Into::<u8>::into(*b)).into()))
-       )
-}
+macro_rules! define_parse_int_be { ($name: ident, $ty: ty) => {
+       fn $name(digits: &[u5]) -> Option<$ty> {
+               digits.iter().fold(Some(Default::default()), |acc, b|
+                       acc
+                               .and_then(|x| x.checked_mul(32))
+                               .and_then(|x| x.checked_add((Into::<u8>::into(*b)).into()))
+               )
+       }
+} }
+define_parse_int_be!(parse_u16_be, u16);
+define_parse_int_be!(parse_u64_be, u64);
 
 fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, Bolt11ParseError> {
        let mut parts = Vec::<RawTaggedField>::new();
@@ -404,7 +407,7 @@ fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, Bolt11ParseErr
 
                // Ignore tag at data[0], it will be handled in the TaggedField parsers and
                // parse the length to find the end of the tagged field's data
-               let len = parse_int_be(&data[1..3], 32).expect("can't overflow");
+               let len = parse_u16_be(&data[1..3]).expect("can't overflow") as usize;
                let last_element = 3 + len;
 
                if data.len() < last_element {
@@ -517,7 +520,7 @@ impl FromBase32 for ExpiryTime {
        type Err = Bolt11ParseError;
 
        fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, Bolt11ParseError> {
-               match parse_int_be::<u64, u5>(field_data, 32)
+               match parse_u64_be(field_data)
                        .map(ExpiryTime::from_seconds)
                {
                        Some(t) => Ok(t),
@@ -530,7 +533,7 @@ impl FromBase32 for MinFinalCltvExpiryDelta {
        type Err = Bolt11ParseError;
 
        fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiryDelta, Bolt11ParseError> {
-               let expiry = parse_int_be::<u64, u5>(field_data, 32);
+               let expiry = parse_u64_be(field_data);
                if let Some(expiry) = expiry {
                        Ok(MinFinalCltvExpiryDelta(expiry))
                } else {
@@ -547,10 +550,10 @@ impl FromBase32 for Fallback {
                        return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
                }
 
-               let version = field_data[0];
+               let version = field_data[0].to_u8();
                let bytes = Vec::<u8>::from_base32(&field_data[1..])?;
 
-               match version.to_u8() {
+               match version {
                        0..=16 => {
                                if bytes.len() < 2 || bytes.len() > 40 {
                                        return Err(Bolt11ParseError::InvalidSegWitProgramLength);
@@ -564,14 +567,14 @@ impl FromBase32 for Fallback {
                        17 => {
                                let pkh = match PubkeyHash::from_slice(&bytes) {
                                        Ok(pkh) => pkh,
-                                       Err(bitcoin::hashes::Error::InvalidLength(_, _)) => return Err(Bolt11ParseError::InvalidPubKeyHashLength),
+                                       Err(_) => return Err(Bolt11ParseError::InvalidPubKeyHashLength),
                                };
                                Ok(Fallback::PubKeyHash(pkh))
                        }
                        18 => {
                                let sh = match ScriptHash::from_slice(&bytes) {
                                        Ok(sh) => sh,
-                                       Err(bitcoin::hashes::Error::InvalidLength(_, _)) => return Err(Bolt11ParseError::InvalidScriptHashLength),
+                                       Err(_) => return Err(Bolt11ParseError::InvalidScriptHashLength),
                                };
                                Ok(Fallback::ScriptHash(sh))
                        }
@@ -602,12 +605,12 @@ impl FromBase32 for PrivateRoute {
 
                        let hop = RouteHintHop {
                                src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
-                               short_channel_id: parse_int_be(&channel_id, 256).expect("short chan ID slice too big?"),
+                               short_channel_id: u64::from_be_bytes(channel_id),
                                fees: RoutingFees {
-                                       base_msat: parse_int_be(&hop_bytes[41..45], 256).expect("slice too big?"),
-                                       proportional_millionths: parse_int_be(&hop_bytes[45..49], 256).expect("slice too big?"),
+                                       base_msat: u32::from_be_bytes(hop_bytes[41..45].try_into().expect("slice too big?")),
+                                       proportional_millionths: u32::from_be_bytes(hop_bytes[45..49].try_into().expect("slice too big?")),
                                },
-                               cltv_expiry_delta: parse_int_be(&hop_bytes[49..51], 256).expect("slice too big?"),
+                               cltv_expiry_delta: u16::from_be_bytes(hop_bytes[49..51].try_into().expect("slice too big?")),
                                htlc_minimum_msat: None,
                                htlc_maximum_msat: None,
                        };
@@ -761,12 +764,16 @@ mod test {
 
        #[test]
        fn test_parse_int_from_bytes_be() {
-               use crate::de::parse_int_be;
-
-               assert_eq!(parse_int_be::<u32, u8>(&[1, 2, 3, 4], 256), Some(16909060));
-               assert_eq!(parse_int_be::<u32, u8>(&[1, 3], 32), Some(35));
-               assert_eq!(parse_int_be::<u32, u8>(&[255, 255, 255, 255], 256), Some(4294967295));
-               assert_eq!(parse_int_be::<u32, u8>(&[1, 0, 0, 0, 0], 256), None);
+               use crate::de::parse_u16_be;
+
+               assert_eq!(parse_u16_be(&[
+                               u5::try_from_u8(1).unwrap(), u5::try_from_u8(2).unwrap(),
+                               u5::try_from_u8(3).unwrap(), u5::try_from_u8(4).unwrap()]
+                       ), Some(34916));
+               assert_eq!(parse_u16_be(&[
+                               u5::try_from_u8(2).unwrap(), u5::try_from_u8(0).unwrap(),
+                               u5::try_from_u8(0).unwrap(), u5::try_from_u8(0).unwrap()]
+                       ), None);
        }
 
        #[test]
@@ -854,8 +861,7 @@ mod test {
        fn test_parse_fallback() {
                use crate::Fallback;
                use bech32::FromBase32;
-               use bitcoin::{PubkeyHash, ScriptHash};
-               use bitcoin::address::WitnessVersion;
+               use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
                use bitcoin::hashes::Hash;
 
                let cases = vec![
@@ -916,7 +922,6 @@ mod test {
                use lightning::routing::router::{RouteHint, RouteHintHop};
                use crate::PrivateRoute;
                use bech32::FromBase32;
-               use crate::de::parse_int_be;
 
                let input = from_bech32(
                        "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
@@ -932,7 +937,7 @@ mod test {
                                        0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
                                ][..]
                        ).unwrap(),
-                       short_channel_id: parse_int_be(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 256).expect("short chan ID slice too big?"),
+                       short_channel_id: 0x0102030405060708,
                        fees: RoutingFees {
                                base_msat: 1,
                                proportional_millionths: 20,
@@ -949,7 +954,7 @@ mod test {
                                        0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
                                ][..]
                        ).unwrap(),
-                       short_channel_id: parse_int_be(&[0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a], 256).expect("short chan ID slice too big?"),
+                       short_channel_id: 0x030405060708090a,
                        fees: RoutingFees {
                                base_msat: 2,
                                proportional_millionths: 30,
index 5b326911444cece5a8b032732f9da24985b2ca25..e427bf3ccb99965a4ba50d8609c3dad186cf3658 100644 (file)
@@ -31,7 +31,6 @@ pub mod utils;
 
 extern crate bech32;
 #[macro_use] extern crate lightning;
-extern crate num_traits;
 extern crate secp256k1;
 extern crate alloc;
 #[cfg(any(test, feature = "std"))]
@@ -43,8 +42,8 @@ extern crate serde;
 use std::time::SystemTime;
 
 use bech32::u5;
-use bitcoin::{Address, Network, PubkeyHash, ScriptHash};
-use bitcoin::address::{Payload, WitnessProgram, WitnessVersion};
+use bitcoin::{Address, Network, PubkeyHash, ScriptHash, WitnessProgram, WitnessVersion};
+use bitcoin::address::Payload;
 use bitcoin::hashes::{Hash, sha256};
 use lightning::ln::features::Bolt11InvoiceFeatures;
 use lightning::util::invoice::construct_invoice_preimage;
@@ -66,7 +65,7 @@ use core::str;
 use serde::{Deserialize, Deserializer,Serialize, Serializer, de::Error};
 
 #[doc(no_inline)]
-pub use lightning::ln::PaymentSecret;
+pub use lightning::ln::types::PaymentSecret;
 #[doc(no_inline)]
 pub use lightning::routing::router::{RouteHint, RouteHintHop};
 #[doc(no_inline)]
@@ -163,7 +162,7 @@ pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA: u64 = 18;
 /// use secp256k1::Secp256k1;
 /// use secp256k1::SecretKey;
 ///
-/// use lightning::ln::PaymentSecret;
+/// use lightning::ln::types::PaymentSecret;
 ///
 /// use lightning_invoice::{Currency, InvoiceBuilder};
 ///
@@ -578,7 +577,13 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool, M: tb::Boo
 
        /// Sets the amount in millisatoshis. The optimal SI prefix is chosen automatically.
        pub fn amount_milli_satoshis(mut self, amount_msat: u64) -> Self {
-               let amount = amount_msat * 10; // Invoices are denominated in "pico BTC"
+               let amount = match amount_msat.checked_mul(10) { // Invoices are denominated in "pico BTC"
+                       Some(amt) => amt,
+                       None => {
+                               self.error = Some(CreationError::InvalidAmount);
+                               return self
+                       }
+               };
                let biggest_possible_si_prefix = SiPrefix::values_desc()
                        .iter()
                        .find(|prefix| amount % prefix.multiplier() == 0)
@@ -873,8 +878,7 @@ impl SignedRawBolt11Invoice {
 
        /// Recovers the public key used for signing the invoice from the recoverable signature.
        pub fn recover_payee_pub_key(&self) -> Result<PayeePubKey, secp256k1::Error> {
-               let hash = Message::from_slice(&self.hash[..])
-                       .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
+               let hash = Message::from_digest(self.hash);
 
                Ok(PayeePubKey(Secp256k1::new().recover_ecdsa(
                        &hash,
@@ -899,8 +903,7 @@ impl SignedRawBolt11Invoice {
                let pub_key = included_pub_key.or(recovered_pub_key.as_ref())
                        .expect("One is always present");
 
-               let hash = Message::from_slice(&self.hash[..])
-                       .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
+               let hash = Message::from_digest(self.hash);
 
                let secp_context = Secp256k1::new();
                let verification_result = secp_context.verify_ecdsa(
@@ -994,8 +997,7 @@ impl RawBolt11Invoice {
                where F: FnOnce(&Message) -> Result<RecoverableSignature, E>
        {
                let raw_hash = self.signable_hash();
-               let hash = Message::from_slice(&raw_hash[..])
-                       .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
+               let hash = Message::from_digest(raw_hash);
                let signature = sign_method(&hash)?;
 
                Ok(SignedRawBolt11Invoice {
@@ -1069,9 +1071,10 @@ impl RawBolt11Invoice {
                find_all_extract!(self.known_tagged_fields(), TaggedField::PrivateRoute(ref x), x).collect()
        }
 
+       /// Returns `None` if no amount is set or on overflow.
        pub fn amount_pico_btc(&self) -> Option<u64> {
-               self.hrp.raw_amount.map(|v| {
-                       v * self.hrp.si_prefix.as_ref().map_or(1_000_000_000_000, |si| { si.multiplier() })
+               self.hrp.raw_amount.and_then(|v| {
+                       v.checked_mul(self.hrp.si_prefix.as_ref().map_or(1_000_000_000_000, |si| { si.multiplier() }))
                })
        }
 
@@ -1877,7 +1880,7 @@ mod test {
                         Bolt11SemanticError};
 
                let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
-               let payment_secret = lightning::ln::PaymentSecret([21; 32]);
+               let payment_secret = lightning::ln::types::PaymentSecret([21; 32]);
                let invoice_template = RawBolt11Invoice {
                        hrp: RawHrp {
                                currency: Currency::Bitcoin,
@@ -2064,7 +2067,7 @@ mod test {
                let route_1 = RouteHint(vec![
                        RouteHintHop {
                                src_node_id: public_key,
-                               short_channel_id: de::parse_int_be(&[123; 8], 256).expect("short chan ID slice too big?"),
+                               short_channel_id: u64::from_be_bytes([123; 8]),
                                fees: RoutingFees {
                                        base_msat: 2,
                                        proportional_millionths: 1,
@@ -2075,7 +2078,7 @@ mod test {
                        },
                        RouteHintHop {
                                src_node_id: public_key,
-                               short_channel_id: de::parse_int_be(&[42; 8], 256).expect("short chan ID slice too big?"),
+                               short_channel_id: u64::from_be_bytes([42; 8]),
                                fees: RoutingFees {
                                        base_msat: 3,
                                        proportional_millionths: 2,
@@ -2100,7 +2103,7 @@ mod test {
                        },
                        RouteHintHop {
                                src_node_id: public_key,
-                               short_channel_id: de::parse_int_be(&[1; 8], 256).expect("short chan ID slice too big?"),
+                               short_channel_id: u64::from_be_bytes([1; 8]),
                                fees: RoutingFees {
                                        base_msat: 5,
                                        proportional_millionths: 4,
index 8196fa9eb89a81d7df74300484ee96cddee5588f..a8ffce7bbd9604d8583da0fdbaf2a2254f749b17 100644 (file)
@@ -12,7 +12,7 @@
 use crate::Bolt11Invoice;
 use bitcoin::hashes::Hash;
 
-use lightning::ln::PaymentHash;
+use lightning::ln::types::PaymentHash;
 use lightning::ln::channelmanager::RecipientOnionFields;
 use lightning::routing::router::{PaymentParameters, RouteParameters};
 
@@ -85,7 +85,7 @@ mod tests {
        use super::*;
        use crate::{InvoiceBuilder, Currency};
        use bitcoin::hashes::sha256::Hash as Sha256;
-       use lightning::ln::PaymentSecret;
+       use lightning::ln::types::PaymentSecret;
        use lightning::routing::router::Payee;
        use secp256k1::{SecretKey, PublicKey, Secp256k1};
        use core::time::Duration;
index dc63783bfa388fc35f7394d8cc243471683df99e..b4f7c778d8af19294499ea4d93cc46fa07daf909 100644 (file)
@@ -329,7 +329,7 @@ impl ToBase32 for Fallback {
        fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
                match *self {
                        Fallback::SegWitProgram {version: v, program: ref p} => {
-                               writer.write_u5(Into::<u5>::into(v))?;
+                               writer.write_u5(u5::try_from_u8(v.to_num()).expect("witness version <= 16"))?;
                                p.write_base32(writer)
                        },
                        Fallback::PubKeyHash(ref hash) => {
index d45a4e8e6462b4256333ea9f6973367b1d586c10..94033bc072cb206e574831a9074d935d72e0b8d6 100644 (file)
@@ -8,7 +8,7 @@ use bitcoin::hashes::Hash;
 use lightning::chain;
 use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
 use lightning::sign::{Recipient, NodeSigner, SignerProvider, EntropySource};
-use lightning::ln::{PaymentHash, PaymentSecret};
+use lightning::ln::types::{PaymentHash, PaymentSecret};
 use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, MIN_FINAL_CLTV_EXPIRY_DELTA};
 use lightning::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA};
 use lightning::ln::inbound_payment::{create, create_from_hash, ExpandedKey};
@@ -19,6 +19,7 @@ use secp256k1::PublicKey;
 use alloc::collections::{btree_map, BTreeMap};
 use core::ops::Deref;
 use core::time::Duration;
+#[cfg(not(feature = "std"))]
 use core::iter::Iterator;
 
 /// Utility to create an invoice that can be paid to one of multiple nodes, or a "phantom invoice."
@@ -823,9 +824,9 @@ mod test {
        use bitcoin::hashes::sha256::Hash as Sha256;
        use lightning::sign::PhantomKeysManager;
        use lightning::events::{MessageSendEvent, MessageSendEventsProvider};
-       use lightning::ln::PaymentHash;
+       use lightning::ln::types::PaymentHash;
        #[cfg(feature = "std")]
-       use lightning::ln::PaymentPreimage;
+       use lightning::ln::types::PaymentPreimage;
        use lightning::ln::channelmanager::{PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY_DELTA, PaymentId, RecipientOnionFields, Retry};
        use lightning::ln::functional_test_utils::*;
        use lightning::ln::msgs::ChannelMessageHandler;
@@ -1396,7 +1397,9 @@ mod test {
                let payment_preimage_opt = if user_generated_pmt_hash { None } else { Some(payment_preimage) };
                assert_eq!(other_events.borrow().len(), 1);
                check_payment_claimable(&other_events.borrow()[0], payment_hash, payment_secret, payment_amt, payment_preimage_opt, invoice.recover_payee_pub_key());
-               do_claim_payment_along_route(&nodes[0], &[&vec!(&nodes[fwd_idx])[..]], false, payment_preimage);
+               do_claim_payment_along_route(
+                       ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[fwd_idx]]], payment_preimage)
+               );
                expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
        }
 
index 92bc87bef63e86e98c7678b88dc2ce251352c874..6b5e99476b02427e254b931260f9b5ee9e9b9cd8 100644 (file)
@@ -4,13 +4,9 @@ extern crate lightning_invoice;
 extern crate secp256k1;
 extern crate hex;
 
-use bitcoin::address::WitnessVersion;
-use bitcoin::{PubkeyHash, ScriptHash};
+use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
 use bitcoin::hashes::hex::FromHex;
 use bitcoin::hashes::{sha256, Hash};
-use lightning::ln::PaymentSecret;
-use lightning::routing::gossip::RoutingFees;
-use lightning::routing::router::{RouteHint, RouteHintHop};
 use lightning_invoice::*;
 use secp256k1::PublicKey;
 use secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
index f779a4395b768d242af25445a6a1c9fc0f520d6c..c340fc8564054d168b41b769abeefb94eec2d96d 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "lightning-net-tokio"
-version = "0.0.121"
+version = "0.0.123-beta"
 authors = ["Matt Corallo"]
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/lightningdevkit/rust-lightning/"
@@ -15,10 +15,10 @@ all-features = true
 rustdoc-args = ["--cfg", "docsrs"]
 
 [dependencies]
-bitcoin = "0.30.2"
-lightning = { version = "0.0.121", path = "../lightning" }
+bitcoin = "0.31.2"
+lightning = { version = "0.0.123-beta", path = "../lightning" }
 tokio = { version = "1.35", features = [ "rt", "sync", "net", "time" ] }
 
 [dev-dependencies]
 tokio = { version = "1.35", features = [ "macros", "rt", "rt-multi-thread", "sync", "net", "time" ] }
-lightning = { version = "0.0.121", path = "../lightning", features = ["_test_utils"] }
+lightning = { version = "0.0.123-beta", path = "../lightning", features = ["_test_utils"] }
index be41a2401244f0bff743e44b9e7ec26482d95227..6d001ca67fd5e1ec44d0307a198509d83cf2538a 100644 (file)
@@ -208,7 +208,12 @@ impl Connection {
                                                break Disconnect::CloseConnection;
                                        }
                                },
-                               SelectorOutput::B(_) => {},
+                               SelectorOutput::B(some) => {
+                                       // The mpsc Receiver should only return `None` if the write side has been
+                                       // dropped, but that shouldn't be possible since its referenced by the Self in
+                                       // `us`.
+                                       debug_assert!(some.is_some());
+                               },
                                SelectorOutput::C(res) => {
                                        if res.is_err() { break Disconnect::PeerDisconnected; }
                                        match reader.try_read(&mut buf) {
@@ -556,7 +561,6 @@ mod tests {
        use lightning::ln::features::*;
        use lightning::ln::msgs::*;
        use lightning::ln::peer_handler::{MessageHandler, PeerManager};
-       use lightning::ln::features::NodeFeatures;
        use lightning::routing::gossip::NodeId;
        use lightning::events::*;
        use lightning::util::test_utils::TestNodeSigner;
@@ -620,8 +624,11 @@ mod tests {
                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_stfu(&self, _their_node_id: &PublicKey, _msg: &Stfu) {}
+               #[cfg(splicing)]
                fn handle_splice(&self, _their_node_id: &PublicKey, _msg: &Splice) {}
+               #[cfg(splicing)]
                fn handle_splice_ack(&self, _their_node_id: &PublicKey, _msg: &SpliceAck) {}
+               #[cfg(splicing)]
                fn handle_splice_locked(&self, _their_node_id: &PublicKey, _msg: &SpliceLocked) {}
                fn handle_tx_add_input(&self, _their_node_id: &PublicKey, _msg: &TxAddInput) {}
                fn handle_tx_add_output(&self, _their_node_id: &PublicKey, _msg: &TxAddOutput) {}
index aad5f2dc1e93676b8a7ca966dbe10a6b07fd92ca..49f63614e83bf4242b67b6fd19b84b322829b021 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "lightning-persister"
-version = "0.0.121"
+version = "0.0.123-beta"
 authors = ["Valentine Wallace", "Matt Corallo"]
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/lightningdevkit/rust-lightning"
@@ -14,8 +14,8 @@ all-features = true
 rustdoc-args = ["--cfg", "docsrs"]
 
 [dependencies]
-bitcoin = "0.30.2"
-lightning = { version = "0.0.121", path = "../lightning" }
+bitcoin = "0.31.2"
+lightning = { version = "0.0.123-beta", path = "../lightning" }
 
 [target.'cfg(windows)'.dependencies]
 windows-sys = { version = "0.48.0", default-features = false, features = ["Win32_Storage_FileSystem", "Win32_Foundation"] }
@@ -24,5 +24,5 @@ windows-sys = { version = "0.48.0", default-features = false, features = ["Win32
 criterion = { version = "0.4", optional = true, default-features = false }
 
 [dev-dependencies]
-lightning = { version = "0.0.121", path = "../lightning", features = ["_test_utils"] }
-bitcoin = { version = "0.30.2", default-features = false }
+lightning = { version = "0.0.123-beta", path = "../lightning", features = ["_test_utils"] }
+bitcoin = { version = "0.31.2", default-features = false }
index 350b1cdd195636f937cd32a700da9464508fee8e..8a144f6196b814104665572366f17c2b7ba02d72 100644 (file)
@@ -379,7 +379,6 @@ mod tests {
        use lightning::ln::functional_test_utils::*;
        use lightning::util::test_utils;
        use lightning::util::persist::read_channel_monitors;
-       use std::fs;
        use std::str::FromStr;
 
        impl Drop for FilesystemStore {
@@ -449,8 +448,6 @@ mod tests {
                nodes[1].node.force_close_broadcasting_latest_txn(&chan.2, &nodes[0].node.get_our_node_id()).unwrap();
                check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed, [nodes[0].node.get_our_node_id()], 100000);
                let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
-               let update_map = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap();
-               let update_id = update_map.get(&added_monitors[0].1.channel_id()).unwrap();
 
                // Set the store's directory to read-only, which should result in
                // returning an unrecoverable failure when we then attempt to persist a
@@ -464,7 +461,7 @@ mod tests {
                        txid: Txid::from_str("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(),
                        index: 0
                };
-               match store.persist_new_channel(test_txo, &added_monitors[0].1, update_id.2) {
+               match store.persist_new_channel(test_txo, &added_monitors[0].1) {
                        ChannelMonitorUpdateStatus::UnrecoverableError => {},
                        _ => panic!("unexpected result from persisting new channel")
                }
@@ -501,7 +498,7 @@ mod tests {
                        txid: Txid::from_str("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(),
                        index: 0
                };
-               match store.persist_new_channel(test_txo, &added_monitors[0].1, update_id.2) {
+               match store.persist_new_channel(test_txo, &added_monitors[0].1) {
                        ChannelMonitorUpdateStatus::UnrecoverableError => {},
                        _ => panic!("unexpected result from persisting new channel")
                }
index 5da607c9fa018bae76340bedbc4bd9454d4302e9..ad25e2f30849de487bf3ad6d8cfce27f68256bb5 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "lightning-rapid-gossip-sync"
-version = "0.0.121"
+version = "0.0.123-beta"
 authors = ["Arik Sosman <git@arik.io>"]
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/lightningdevkit/rust-lightning"
@@ -15,11 +15,11 @@ no-std = ["lightning/no-std"]
 std = ["lightning/std"]
 
 [dependencies]
-lightning = { version = "0.0.121", path = "../lightning", default-features = false }
-bitcoin = { version = "0.30.2", default-features = false }
+lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false }
+bitcoin = { version = "0.31.2", default-features = false }
 
 [target.'cfg(ldk_bench)'.dependencies]
 criterion = { version = "0.4", optional = true, default-features = false }
 
 [dev-dependencies]
-lightning = { version = "0.0.121", path = "../lightning", features = ["_test_utils"] }
+lightning = { version = "0.0.123-beta", path = "../lightning", features = ["_test_utils"] }
index 9023b9ba38ca52c16d003bebaa21e1511c946d2e..b3fae0ffd8bea8e19822c5a2cf9bc19d3fe33939 100644 (file)
@@ -19,7 +19,7 @@ use crate::{GraphSyncError, RapidGossipSync};
 #[cfg(all(feature = "std", not(test)))]
 use std::time::{SystemTime, UNIX_EPOCH};
 
-#[cfg(not(feature = "std"))]
+#[cfg(all(not(feature = "std"), not(test)))]
 use alloc::{vec::Vec, borrow::ToOwned};
 
 /// The purpose of this prefix is to identify the serialization format, should other rapid gossip
index 4118d3012ef5e4547de5ac165ac9e2382bc6ab90..23ead252a81ae5b1f8b58b90aed4c43a1a925674 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "lightning-transaction-sync"
-version = "0.0.121"
+version = "0.0.123-beta"
 authors = ["Elias Rohrer"]
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/lightningdevkit/rust-lightning"
@@ -23,19 +23,19 @@ electrum = ["electrum-client"]
 async-interface = []
 
 [dependencies]
-lightning = { version = "0.0.121", path = "../lightning", default-features = false, features = ["std"] }
-bitcoin = { version = "0.30.2", default-features = false }
+lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false, features = ["std"] }
+bitcoin = { version = "0.31.2", default-features = false }
 bdk-macros = "0.6"
 futures = { version = "0.3", optional = true }
-esplora-client = { version = "0.6", default-features = false, optional = true }
-electrum-client = { version = "0.18.0", optional = true }
+esplora-client = { version = "0.7", default-features = false, optional = true }
+electrum-client = { version = "0.19.0", optional = true }
 
 [dev-dependencies]
-lightning = { version = "0.0.121", path = "../lightning", default-features = false, features = ["std", "_test_utils"] }
+lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false, features = ["std", "_test_utils"] }
 tokio = { version = "1.35.0", features = ["full"] }
 
 [target.'cfg(all(not(target_os = "windows"), not(no_download)))'.dev-dependencies]
-electrsd = { version = "0.26.0", default-features = false, features = ["legacy", "esplora_a33e97e1", "bitcoind_25_0"] }
+electrsd = { version = "0.27.3", default-features = false, features = ["legacy", "esplora_a33e97e1", "bitcoind_25_0"] }
 
 [target.'cfg(all(not(target_os = "windows"), no_download))'.dev-dependencies]
-electrsd = { version = "0.26.0", default-features = false, features = ["legacy"] }
+electrsd = { version = "0.27.3", default-features = false, features = ["legacy"] }
index be49fbe96ff017aa73d0c8e87265bb442b50fffc..c635f7385c6ecbd47bb6b4e90f5abb335feca8f5 100644 (file)
@@ -1,4 +1,5 @@
 use lightning::chain::{Confirm, WatchedOutput};
+use lightning::chain::channelmonitor::ANTI_REORG_DELAY;
 use bitcoin::{Txid, BlockHash, Transaction, OutPoint};
 use bitcoin::block::Header;
 
@@ -13,6 +14,9 @@ pub(crate) struct SyncState {
        // Outputs that were previously processed, but must not be forgotten yet as
        // as we still need to monitor any spends on-chain.
        pub watched_outputs: HashMap<OutPoint, WatchedOutput>,
+       // Outputs for which we previously saw a spend on-chain but kept around until the spends reach
+       // sufficient depth.
+       pub outputs_spends_pending_threshold_conf: Vec<(Txid, u32, OutPoint, WatchedOutput)>,
        // The tip hash observed during our last sync.
        pub last_sync_hash: Option<BlockHash>,
        // Indicates whether we need to resync, e.g., after encountering an error.
@@ -24,6 +28,7 @@ impl SyncState {
                Self {
                        watched_transactions: HashSet::new(),
                        watched_outputs: HashMap::new(),
+                       outputs_spends_pending_threshold_conf: Vec::new(),
                        last_sync_hash: None,
                        pending_sync: false,
                }
@@ -38,6 +43,17 @@ impl SyncState {
                        }
 
                        self.watched_transactions.insert(txid);
+
+                       // If a previously-confirmed output spend is unconfirmed, re-add the watched output to
+                       // the tracking map.
+                       self.outputs_spends_pending_threshold_conf.retain(|(conf_txid, _, prev_outpoint, output)| {
+                               if txid == *conf_txid {
+                                       self.watched_outputs.insert(*prev_outpoint, output.clone());
+                                       false
+                               } else {
+                                       true
+                               }
+                       })
                }
        }
 
@@ -57,10 +73,18 @@ impl SyncState {
                        self.watched_transactions.remove(&ctx.tx.txid());
 
                        for input in &ctx.tx.input {
-                               self.watched_outputs.remove(&input.previous_output);
+                               if let Some(output) = self.watched_outputs.remove(&input.previous_output) {
+                                       self.outputs_spends_pending_threshold_conf.push((ctx.tx.txid(), ctx.block_height, input.previous_output, output));
+                               }
                        }
                }
        }
+
+       pub fn prune_output_spends(&mut self, cur_height: u32) {
+               self.outputs_spends_pending_threshold_conf.retain(|(_, conf_height, _, _)| {
+                       cur_height < conf_height + ANTI_REORG_DELAY - 1
+               });
+       }
 }
 
 
@@ -104,6 +128,7 @@ impl FilterQueue {
 #[derive(Debug)]
 pub(crate) struct ConfirmedTx {
        pub tx: Transaction,
+       pub txid: Txid,
        pub block_header: Header,
        pub block_height: u32,
        pub pos: usize,
index d0c8afef77e3161ec191f41a0ceae4aa5eb8efe0..046f698d4a259e3b0eace68790e59cfe1d406eb7 100644 (file)
@@ -157,6 +157,9 @@ where
                                        for c in &confirmables {
                                                c.best_block_updated(&tip_header, tip_height);
                                        }
+
+                                       // Prune any sufficiently confirmed output spends
+                                       sync_state.prune_output_spends(tip_height);
                                }
 
                                match self.get_confirmed_transactions(&sync_state) {
@@ -254,7 +257,7 @@ where
 
                // First, check the confirmation status of registered transactions as well as the
                // status of dependent transactions of registered outputs.
-               let mut confirmed_txs = Vec::new();
+               let mut confirmed_txs: Vec<ConfirmedTx> = Vec::new();
                let mut watched_script_pubkeys = Vec::with_capacity(
                        sync_state.watched_transactions.len() + sync_state.watched_outputs.len());
                let mut watched_txs = Vec::with_capacity(sync_state.watched_transactions.len());
@@ -302,6 +305,9 @@ where
 
                                for (i, script_history) in tx_results.iter().enumerate() {
                                        let (txid, tx) = &watched_txs[i];
+                                       if confirmed_txs.iter().any(|ctx| ctx.txid == **txid) {
+                                               continue;
+                                       }
                                        let mut filtered_history = script_history.iter().filter(|h| h.tx_hash == **txid);
                                        if let Some(history) = filtered_history.next()
                                        {
@@ -321,6 +327,10 @@ where
                                                }
 
                                                let txid = possible_output_spend.tx_hash;
+                                               if confirmed_txs.iter().any(|ctx| ctx.txid == txid) {
+                                                       continue;
+                                               }
+
                                                match self.client.transaction_get(&txid) {
                                                        Ok(tx) => {
                                                                let mut is_spend = false;
@@ -416,6 +426,7 @@ where
                                                }
                                                let confirmed_tx = ConfirmedTx {
                                                        tx: tx.clone(),
+                                                       txid,
                                                        block_header, block_height: prob_conf_height,
                                                        pos,
                                                };
index eb52faf33648cfb173985b13c29ecb9d754fdb87..681075ade5afdbfb1176b52d3751d8eddaec5172 100644 (file)
@@ -51,7 +51,7 @@ where
        pub fn new(server_url: String, logger: L) -> Self {
                let builder = Builder::new(&server_url);
                #[cfg(not(feature = "async-interface"))]
-               let client = builder.build_blocking().unwrap();
+               let client = builder.build_blocking();
                #[cfg(feature = "async-interface")]
                let client = builder.build_async().unwrap();
 
@@ -153,7 +153,7 @@ where
                                                }
                                        }
 
-                                       match maybe_await!(self.sync_best_block_updated(&confirmables, &tip_hash)) {
+                                       match maybe_await!(self.sync_best_block_updated(&confirmables, &mut sync_state, &tip_hash)) {
                                                Ok(()) => {}
                                                Err(InternalError::Inconsistency) => {
                                                        // Immediately restart syncing when we encounter any inconsistencies.
@@ -238,7 +238,7 @@ where
 
        #[maybe_async]
        fn sync_best_block_updated(
-               &self, confirmables: &Vec<&(dyn Confirm + Sync + Send)>, tip_hash: &BlockHash,
+               &self, confirmables: &Vec<&(dyn Confirm + Sync + Send)>, sync_state: &mut SyncState, tip_hash: &BlockHash,
        ) -> Result<(), InternalError> {
 
                // Inform the interface of the new block.
@@ -249,6 +249,9 @@ where
                                for c in confirmables {
                                        c.best_block_updated(&tip_header, tip_height);
                                }
+
+                               // Prune any sufficiently confirmed output spends
+                               sync_state.prune_output_spends(tip_height);
                        }
                } else {
                        return Err(InternalError::Inconsistency);
@@ -264,10 +267,13 @@ where
                // First, check the confirmation status of registered transactions as well as the
                // status of dependent transactions of registered outputs.
 
-               let mut confirmed_txs = Vec::new();
+               let mut confirmed_txs: Vec<ConfirmedTx> = Vec::new();
 
                for txid in &sync_state.watched_transactions {
-                       if let Some(confirmed_tx) = maybe_await!(self.get_confirmed_tx(&txid, None, None))? {
+                       if confirmed_txs.iter().any(|ctx| ctx.txid == *txid) {
+                               continue;
+                       }
+                       if let Some(confirmed_tx) = maybe_await!(self.get_confirmed_tx(*txid, None, None))? {
                                confirmed_txs.push(confirmed_tx);
                        }
                }
@@ -278,9 +284,19 @@ where
                        {
                                if let Some(spending_txid) = output_status.txid {
                                        if let Some(spending_tx_status) = output_status.status {
+                                               if confirmed_txs.iter().any(|ctx| ctx.txid == spending_txid) {
+                                                       if spending_tx_status.confirmed {
+                                                               // Skip inserting duplicate ConfirmedTx entry
+                                                               continue;
+                                                       } else {
+                                                               log_trace!(self.logger, "Inconsistency: Detected previously-confirmed Tx {} as unconfirmed", spending_txid);
+                                                               return Err(InternalError::Inconsistency);
+                                                       }
+                                               }
+
                                                if let Some(confirmed_tx) = maybe_await!(self
                                                        .get_confirmed_tx(
-                                                               &spending_txid,
+                                                               spending_txid,
                                                                spending_tx_status.block_hash,
                                                                spending_tx_status.block_height,
                                                        ))?
@@ -303,7 +319,7 @@ where
 
        #[maybe_async]
        fn get_confirmed_tx(
-               &self, txid: &Txid, expected_block_hash: Option<BlockHash>, known_block_height: Option<u32>,
+               &self, txid: Txid, expected_block_hash: Option<BlockHash>, known_block_height: Option<u32>,
        ) -> Result<Option<ConfirmedTx>, InternalError> {
                if let Some(merkle_block) = maybe_await!(self.client.get_merkle_block(&txid))? {
                        let block_header = merkle_block.header;
@@ -318,7 +334,7 @@ where
                        let mut matches = Vec::new();
                        let mut indexes = Vec::new();
                        let _ = merkle_block.txn.extract_matches(&mut matches, &mut indexes);
-                       if indexes.len() != 1 || matches.len() != 1 || matches[0] != *txid {
+                       if indexes.len() != 1 || matches.len() != 1 || matches[0] != txid {
                                log_error!(self.logger, "Retrieved Merkle block for txid {} doesn't match expectations. This should not happen. Please verify server integrity.", txid);
                                return Err(InternalError::Failed);
                        }
@@ -326,14 +342,19 @@ where
                        // unwrap() safety: len() > 0 is checked above
                        let pos = *indexes.first().unwrap() as usize;
                        if let Some(tx) = maybe_await!(self.client.get_tx(&txid))? {
+                               if tx.txid() != txid {
+                                       log_error!(self.logger, "Retrieved transaction for txid {} doesn't match expectations. This should not happen. Please verify server integrity.", txid);
+                                       return Err(InternalError::Failed);
+                               }
+
                                if let Some(block_height) = known_block_height {
                                        // We can take a shortcut here if a previous call already gave us the height.
-                                       return Ok(Some(ConfirmedTx { tx, block_header, pos, block_height }));
+                                       return Ok(Some(ConfirmedTx { tx, txid, block_header, pos, block_height }));
                                }
 
                                let block_status = maybe_await!(self.client.get_block_status(&block_hash))?;
                                if let Some(block_height) = block_status.height {
-                                       return Ok(Some(ConfirmedTx { tx, block_header, pos, block_height }));
+                                       return Ok(Some(ConfirmedTx { tx, txid, block_header, pos, block_height }));
                                } else {
                                        // If any previously-confirmed block suddenly is no longer confirmed, we found
                                        // an inconsistency and should start over.
index 48044b236bf8c1e6360334d40b6c729dca468a03..b1118e457767f533ed37b6d68bece48002f80975 100644 (file)
@@ -12,7 +12,7 @@ use electrsd::{bitcoind, bitcoind::BitcoinD, ElectrsD};
 use bitcoin::{Amount, Txid, BlockHash};
 use bitcoin::blockdata::block::Header;
 use bitcoin::blockdata::constants::genesis_block;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 use electrsd::bitcoind::bitcoincore_rpc::bitcoincore_rpc_json::AddressType;
 use bitcoind::bitcoincore_rpc::RpcApi;
 use bdk_macros::maybe_await;
@@ -171,7 +171,7 @@ macro_rules! test_syncing {
                None, None, None, None).unwrap();
                let second_txid = $bitcoind.client.send_to_address(&new_address, Amount::from_sat(5000), None,
                None, None, None, None, None).unwrap();
-               $tx_sync.register_tx(&txid, &new_address.payload.script_pubkey());
+               $tx_sync.register_tx(&txid, &new_address.payload().script_pubkey());
 
                maybe_await!($tx_sync.sync(vec![&$confirmable])).unwrap();
 
index 96070e4d0dd23246976b22004954d38704cddeb9..8f238f5b5573f6e9269b6950df784255cfc4cc7a 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "lightning"
-version = "0.0.121"
+version = "0.0.123-beta"
 authors = ["Matt Corallo"]
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/lightningdevkit/rust-lightning/"
@@ -32,7 +32,7 @@ unsafe_revoked_tx_signing = []
 _test_vectors = []
 
 no-std = ["hashbrown", "possiblyrandom", "bitcoin/no-std", "core2/alloc", "libm"]
-std = ["bitcoin/std"]
+std = ["bitcoin/std", "bech32/std"]
 
 # Generates low-r bitcoin signatures, which saves 1 byte in 50% of the cases
 grind_signatures = []
@@ -40,10 +40,11 @@ grind_signatures = []
 default = ["std", "grind_signatures"]
 
 [dependencies]
-bitcoin = { version = "0.30.2", default-features = false, features = ["secp-recovery"] }
+bech32 = { version = "0.9.1", default-features = false }
+bitcoin = { version = "0.31.2", default-features = false, features = ["secp-recovery"] }
 
 hashbrown = { version = "0.13", optional = true, default-features = false }
-possiblyrandom = { version = "0.1", optional = true, default-features = false }
+possiblyrandom = { version = "0.2", optional = true, default-features = false }
 hex = { package = "hex-conservative", version = "0.1.1", default-features = false }
 regex = { version = "1.5.6", optional = true }
 backtrace = { version = "0.3", optional = true }
@@ -55,7 +56,7 @@ libm = { version = "0.2", optional = true, default-features = false }
 regex = "1.5.6"
 
 [dev-dependencies.bitcoin]
-version = "0.30.2"
+version = "0.31.2"
 default-features = false
 features = ["bitcoinconsensus", "secp-recovery"]
 
@@ -63,4 +64,4 @@ features = ["bitcoinconsensus", "secp-recovery"]
 criterion = { version = "0.4", optional = true, default-features = false }
 
 [target.'cfg(taproot)'.dependencies]
-musig2 = { git = "https://github.com/arik-so/rust-musig2", rev = "cff11e3" }
+musig2 = { git = "https://github.com/arik-so/rust-musig2", rev = "739533fc" }
index 3a5541fa1468272f3859972bca2cdfb4f8053ee8..1f3f5a1fa38e70bdac2c7d01923fa06e04513f2e 100644 (file)
@@ -1,12 +1,27 @@
+// 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.
+
+//! Data structures and methods for constructing [`BlindedPath`]s to send a message over.
+//!
+//! [`BlindedPath`]: crate::blinded_path::BlindedPath
+
 use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
 
-use crate::blinded_path::{BlindedHop, BlindedPath};
+#[allow(unused_imports)]
+use crate::prelude::*;
+
+use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode, NextMessageHop, NodeIdLookUp};
 use crate::blinded_path::utils;
 use crate::io;
 use crate::io::Cursor;
 use crate::ln::onion_utils;
 use crate::onion_message::packet::ControlTlvs;
-use crate::prelude::*;
 use crate::sign::{NodeSigner, Recipient};
 use crate::crypto::streams::ChaChaPolyReadAdapter;
 use crate::util::ser::{FixedLengthReader, LengthReadableArgs, Writeable, Writer};
@@ -14,11 +29,22 @@ use crate::util::ser::{FixedLengthReader, LengthReadableArgs, Writeable, Writer}
 use core::mem;
 use core::ops::Deref;
 
+/// An intermediate node, and possibly a short channel id leading to the next node.
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+pub struct ForwardNode {
+       /// This node's pubkey.
+       pub node_id: PublicKey,
+       /// The channel between `node_id` and the next hop. If set, the constructed [`BlindedHop`]'s
+       /// `encrypted_payload` will use this instead of the next [`ForwardNode::node_id`] for a more
+       /// compact representation.
+       pub short_channel_id: Option<u64>,
+}
+
 /// TLVs to encode in an intermediate onion message packet's hop data. When provided in a blinded
 /// route, they are encoded into [`BlindedHop::encrypted_payload`].
 pub(crate) struct ForwardTlvs {
-       /// The node id of the next hop in the onion message's path.
-       pub(crate) next_node_id: PublicKey,
+       /// The next hop in the onion message's path.
+       pub(crate) next_hop: NextMessageHop,
        /// Senders to a blinded path use this value to concatenate the route they find to the
        /// introduction node with the blinded path.
        pub(crate) next_blinding_override: Option<PublicKey>,
@@ -34,9 +60,14 @@ pub(crate) struct ReceiveTlvs {
 
 impl Writeable for ForwardTlvs {
        fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+               let (next_node_id, short_channel_id) = match self.next_hop {
+                       NextMessageHop::NodeId(pubkey) => (Some(pubkey), None),
+                       NextMessageHop::ShortChannelId(scid) => (None, Some(scid)),
+               };
                // TODO: write padding
                encode_tlv_stream!(writer, {
-                       (4, self.next_node_id, required),
+                       (2, short_channel_id, option),
+                       (4, next_node_id, option),
                        (8, self.next_blinding_override, option)
                });
                Ok(())
@@ -53,34 +84,52 @@ impl Writeable for ReceiveTlvs {
        }
 }
 
-/// Construct blinded onion message hops for the given `unblinded_path`.
+/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
 pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
-       secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], session_priv: &SecretKey
+       secp_ctx: &Secp256k1<T>, intermediate_nodes: &[ForwardNode], recipient_node_id: PublicKey,
+       session_priv: &SecretKey
 ) -> Result<Vec<BlindedHop>, secp256k1::Error> {
-       let blinded_tlvs = unblinded_path.iter()
+       let pks = intermediate_nodes.iter().map(|node| &node.node_id)
+               .chain(core::iter::once(&recipient_node_id));
+       let tlvs = pks.clone()
                .skip(1) // The first node's TLVs contains the next node's pubkey
-               .map(|pk| {
-                       ControlTlvs::Forward(ForwardTlvs { next_node_id: *pk, next_blinding_override: None })
+               .zip(intermediate_nodes.iter().map(|node| node.short_channel_id))
+               .map(|(pubkey, scid)| match scid {
+                       Some(scid) => NextMessageHop::ShortChannelId(scid),
+                       None => NextMessageHop::NodeId(*pubkey),
                })
+               .map(|next_hop| ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None }))
                .chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { path_id: None })));
 
-       utils::construct_blinded_hops(secp_ctx, unblinded_path.iter(), blinded_tlvs, session_priv)
+       utils::construct_blinded_hops(secp_ctx, pks, tlvs, session_priv)
 }
 
 // Advance the blinded onion message path by one hop, so make the second hop into the new
 // introduction node.
-pub(crate) fn advance_path_by_one<NS: Deref, T: secp256k1::Signing + secp256k1::Verification>(
-       path: &mut BlindedPath, node_signer: &NS, secp_ctx: &Secp256k1<T>
-) -> Result<(), ()> where NS::Target: NodeSigner {
+pub(crate) fn advance_path_by_one<NS: Deref, NL: Deref, T>(
+       path: &mut BlindedPath, node_signer: &NS, node_id_lookup: &NL, secp_ctx: &Secp256k1<T>
+) -> Result<(), ()>
+where
+       NS::Target: NodeSigner,
+       NL::Target: NodeIdLookUp,
+       T: secp256k1::Signing + secp256k1::Verification,
+{
        let control_tlvs_ss = node_signer.ecdh(Recipient::Node, &path.blinding_point, None)?;
        let rho = onion_utils::gen_rho_from_shared_secret(&control_tlvs_ss.secret_bytes());
        let encrypted_control_tlvs = path.blinded_hops.remove(0).encrypted_payload;
        let mut s = Cursor::new(&encrypted_control_tlvs);
        let mut reader = FixedLengthReader::new(&mut s, encrypted_control_tlvs.len() as u64);
        match ChaChaPolyReadAdapter::read(&mut reader, rho) {
-               Ok(ChaChaPolyReadAdapter { readable: ControlTlvs::Forward(ForwardTlvs {
-                       mut next_node_id, next_blinding_override,
-               })}) => {
+               Ok(ChaChaPolyReadAdapter {
+                       readable: ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override })
+               }) => {
+                       let next_node_id = match next_hop {
+                               NextMessageHop::NodeId(pubkey) => pubkey,
+                               NextMessageHop::ShortChannelId(scid) => match node_id_lookup.next_node_id(scid) {
+                                       Some(pubkey) => pubkey,
+                                       None => return Err(()),
+                               },
+                       };
                        let mut new_blinding_point = match next_blinding_override {
                                Some(blinding_point) => blinding_point,
                                None => {
@@ -89,7 +138,7 @@ pub(crate) fn advance_path_by_one<NS: Deref, T: secp256k1::Signing + secp256k1::
                                }
                        };
                        mem::swap(&mut path.blinding_point, &mut new_blinding_point);
-                       mem::swap(&mut path.introduction_node_id, &mut next_node_id);
+                       path.introduction_node = IntroductionNode::NodeId(next_node_id);
                        Ok(())
                },
                _ => Err(())
index e70f310f5e1d8b00875ff58e069f432bd69526cf..17ea67c536544cb1e3cbf4c04faf1fee1b6d5c97 100644 (file)
 //! Creating blinded paths and related utilities live here.
 
 pub mod payment;
-pub(crate) mod message;
+pub mod message;
 pub(crate) mod utils;
 
 use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
+use core::ops::Deref;
 
 use crate::ln::msgs::DecodeError;
 use crate::offers::invoice::BlindedPayInfo;
+use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph};
 use crate::sign::EntropySource;
 use crate::util::ser::{Readable, Writeable, Writer};
+use crate::util::scid_utils;
 
 use crate::io;
 use crate::prelude::*;
 
+/// The next hop to forward an onion message along its path.
+///
+/// Note that payment blinded paths always specify their next hop using an explicit node id.
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+pub enum NextMessageHop {
+       /// The node id of the next hop.
+       NodeId(PublicKey),
+       /// The short channel id leading to the next hop.
+       ShortChannelId(u64),
+}
+
 /// Onion messages and payments can be sent and received to blinded paths, which serve to hide the
 /// identity of the recipient.
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
 pub struct BlindedPath {
        /// To send to a blinded path, the sender first finds a route to the unblinded
-       /// `introduction_node_id`, which can unblind its [`encrypted_payload`] to find out the onion
+       /// `introduction_node`, which can unblind its [`encrypted_payload`] to find out the onion
        /// message or payment's next hop and forward it along.
        ///
        /// [`encrypted_payload`]: BlindedHop::encrypted_payload
-       pub introduction_node_id: PublicKey,
+       pub introduction_node: IntroductionNode,
        /// Used by the introduction node to decrypt its [`encrypted_payload`] to forward the onion
        /// message or payment.
        ///
@@ -42,6 +56,57 @@ pub struct BlindedPath {
        pub blinded_hops: Vec<BlindedHop>,
 }
 
+/// The unblinded node in a [`BlindedPath`].
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+pub enum IntroductionNode {
+       /// The node id of the introduction node.
+       NodeId(PublicKey),
+       /// The short channel id of the channel leading to the introduction node. The [`Direction`]
+       /// identifies which side of the channel is the introduction node.
+       DirectedShortChannelId(Direction, u64),
+}
+
+/// The side of a channel that is the [`IntroductionNode`] in a [`BlindedPath`]. [BOLT 7] defines
+/// which nodes is which in the [`ChannelAnnouncement`] message.
+///
+/// [BOLT 7]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#the-channel_announcement-message
+/// [`ChannelAnnouncement`]: crate::ln::msgs::ChannelAnnouncement
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
+pub enum Direction {
+       /// The lesser node id when compared lexicographically in ascending order.
+       NodeOne,
+       /// The greater node id when compared lexicographically in ascending order.
+       NodeTwo,
+}
+
+/// An interface for looking up the node id of a channel counterparty for the purpose of forwarding
+/// an [`OnionMessage`].
+///
+/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
+pub trait NodeIdLookUp {
+       /// Returns the node id of the forwarding node's channel counterparty with `short_channel_id`.
+       ///
+       /// Here, the forwarding node is referring to the node of the [`OnionMessenger`] parameterized
+       /// by the [`NodeIdLookUp`] and the counterparty to one of that node's peers.
+       ///
+       /// [`OnionMessenger`]: crate::onion_message::messenger::OnionMessenger
+       fn next_node_id(&self, short_channel_id: u64) -> Option<PublicKey>;
+}
+
+/// A [`NodeIdLookUp`] that always returns `None`.
+pub struct EmptyNodeIdLookUp {}
+
+impl NodeIdLookUp for EmptyNodeIdLookUp {
+       fn next_node_id(&self, _short_channel_id: u64) -> Option<PublicKey> {
+               None
+       }
+}
+
+impl Deref for EmptyNodeIdLookUp {
+       type Target = EmptyNodeIdLookUp;
+       fn deref(&self) -> &Self { self }
+}
+
 /// An encrypted payload and node id corresponding to a hop in a payment or onion message path, to
 /// be encoded in the sender's onion packet. These hops cannot be identified by outside observers
 /// and thus can be used to hide the identity of the recipient.
@@ -57,10 +122,10 @@ pub struct BlindedHop {
 
 impl BlindedPath {
        /// Create a one-hop blinded path for a message.
-       pub fn one_hop_for_message<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
-               recipient_node_id: PublicKey, entropy_source: &ES, secp_ctx: &Secp256k1<T>
-       ) -> Result<Self, ()> {
-               Self::new_for_message(&[recipient_node_id], entropy_source, secp_ctx)
+       pub fn one_hop_for_message<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
+               recipient_node_id: PublicKey, entropy_source: ES, secp_ctx: &Secp256k1<T>
+       ) -> Result<Self, ()> where ES::Target: EntropySource {
+               Self::new_for_message(&[], recipient_node_id, entropy_source, secp_ctx)
        }
 
        /// Create a blinded path for an onion message, to be forwarded along `node_pks`. The last node
@@ -68,26 +133,30 @@ impl BlindedPath {
        ///
        /// Errors if no hops are provided or if `node_pk`(s) are invalid.
        //  TODO: make all payloads the same size with padding + add dummy hops
-       pub fn new_for_message<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
-               node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1<T>
-       ) -> Result<Self, ()> {
-               if node_pks.is_empty() { return Err(()) }
+       pub fn new_for_message<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
+               intermediate_nodes: &[message::ForwardNode], recipient_node_id: PublicKey,
+               entropy_source: ES, secp_ctx: &Secp256k1<T>
+       ) -> Result<Self, ()> where ES::Target: EntropySource {
+               let introduction_node = IntroductionNode::NodeId(
+                       intermediate_nodes.first().map_or(recipient_node_id, |n| n.node_id)
+               );
                let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
                let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
-               let introduction_node_id = node_pks[0];
 
                Ok(BlindedPath {
-                       introduction_node_id,
+                       introduction_node,
                        blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
-                       blinded_hops: message::blinded_hops(secp_ctx, node_pks, &blinding_secret).map_err(|_| ())?,
+                       blinded_hops: message::blinded_hops(
+                               secp_ctx, intermediate_nodes, recipient_node_id, &blinding_secret,
+                       ).map_err(|_| ())?,
                })
        }
 
        /// Create a one-hop blinded path for a payment.
-       pub fn one_hop_for_payment<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
+       pub fn one_hop_for_payment<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
                payee_node_id: PublicKey, payee_tlvs: payment::ReceiveTlvs, min_final_cltv_expiry_delta: u16,
-               entropy_source: &ES, secp_ctx: &Secp256k1<T>
-       ) -> Result<(BlindedPayInfo, Self), ()> {
+               entropy_source: ES, secp_ctx: &Secp256k1<T>
+       ) -> Result<(BlindedPayInfo, Self), ()> where ES::Target: EntropySource {
                // This value is not considered in pathfinding for 1-hop blinded paths, because it's intended to
                // be in relation to a specific channel.
                let htlc_maximum_msat = u64::max_value();
@@ -106,11 +175,14 @@ impl BlindedPath {
        ///
        /// [`ForwardTlvs`]: crate::blinded_path::payment::ForwardTlvs
        //  TODO: make all payloads the same size with padding + add dummy hops
-       pub fn new_for_payment<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
+       pub fn new_for_payment<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
                intermediate_nodes: &[payment::ForwardNode], payee_node_id: PublicKey,
                payee_tlvs: payment::ReceiveTlvs, htlc_maximum_msat: u64, min_final_cltv_expiry_delta: u16,
-               entropy_source: &ES, secp_ctx: &Secp256k1<T>
-       ) -> Result<(BlindedPayInfo, Self), ()> {
+               entropy_source: ES, secp_ctx: &Secp256k1<T>
+       ) -> Result<(BlindedPayInfo, Self), ()> where ES::Target: EntropySource {
+               let introduction_node = IntroductionNode::NodeId(
+                       intermediate_nodes.first().map_or(payee_node_id, |n| n.node_id)
+               );
                let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
                let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
 
@@ -118,18 +190,78 @@ impl BlindedPath {
                        intermediate_nodes, &payee_tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta
                )?;
                Ok((blinded_payinfo, BlindedPath {
-                       introduction_node_id: intermediate_nodes.first().map_or(payee_node_id, |n| n.node_id),
+                       introduction_node,
                        blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
                        blinded_hops: payment::blinded_hops(
                                secp_ctx, intermediate_nodes, payee_node_id, payee_tlvs, &blinding_secret
                        ).map_err(|_| ())?,
                }))
        }
+
+       /// Returns the introduction [`NodeId`] of the blinded path, if it is publicly reachable (i.e.,
+       /// it is found in the network graph).
+       pub fn public_introduction_node_id<'a>(
+               &self, network_graph: &'a ReadOnlyNetworkGraph
+       ) -> Option<&'a NodeId> {
+               match &self.introduction_node {
+                       IntroductionNode::NodeId(pubkey) => {
+                               let node_id = NodeId::from_pubkey(pubkey);
+                               network_graph.nodes().get_key_value(&node_id).map(|(key, _)| key)
+                       },
+                       IntroductionNode::DirectedShortChannelId(direction, scid) => {
+                               network_graph
+                                       .channel(*scid)
+                                       .map(|c| match direction {
+                                               Direction::NodeOne => &c.node_one,
+                                               Direction::NodeTwo => &c.node_two,
+                                       })
+                       },
+               }
+       }
+
+       /// Attempts to a use a compact representation for the [`IntroductionNode`] by using a directed
+       /// short channel id from a channel in `network_graph` leading to the introduction node.
+       ///
+       /// While this may result in a smaller encoding, there is a trade off in that the path may
+       /// become invalid if the channel is closed or hasn't been propagated via gossip. Therefore,
+       /// calling this may not be suitable for long-lived blinded paths.
+       pub fn use_compact_introduction_node(&mut self, network_graph: &ReadOnlyNetworkGraph) {
+               if let IntroductionNode::NodeId(pubkey) = &self.introduction_node {
+                       let node_id = NodeId::from_pubkey(pubkey);
+                       if let Some(node_info) = network_graph.node(&node_id) {
+                               if let Some((scid, channel_info)) = node_info
+                                       .channels
+                                       .iter()
+                                       .filter_map(|scid| network_graph.channel(*scid).map(|info| (*scid, info)))
+                                       .min_by_key(|(scid, _)| scid_utils::block_from_scid(*scid))
+                               {
+                                       let direction = if node_id == channel_info.node_one {
+                                               Direction::NodeOne
+                                       } else {
+                                               debug_assert_eq!(node_id, channel_info.node_two);
+                                               Direction::NodeTwo
+                                       };
+                                       self.introduction_node =
+                                               IntroductionNode::DirectedShortChannelId(direction, scid);
+                               }
+                       }
+               }
+       }
 }
 
 impl Writeable for BlindedPath {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
-               self.introduction_node_id.write(w)?;
+               match &self.introduction_node {
+                       IntroductionNode::NodeId(pubkey) => pubkey.write(w)?,
+                       IntroductionNode::DirectedShortChannelId(direction, scid) => {
+                               match direction {
+                                       Direction::NodeOne => 0u8.write(w)?,
+                                       Direction::NodeTwo => 1u8.write(w)?,
+                               }
+                               scid.write(w)?;
+                       },
+               }
+
                self.blinding_point.write(w)?;
                (self.blinded_hops.len() as u8).write(w)?;
                for hop in &self.blinded_hops {
@@ -141,7 +273,17 @@ impl Writeable for BlindedPath {
 
 impl Readable for BlindedPath {
        fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
-               let introduction_node_id = Readable::read(r)?;
+               let mut first_byte: u8 = Readable::read(r)?;
+               let introduction_node = match first_byte {
+                       0 => IntroductionNode::DirectedShortChannelId(Direction::NodeOne, Readable::read(r)?),
+                       1 => IntroductionNode::DirectedShortChannelId(Direction::NodeTwo, Readable::read(r)?),
+                       2|3 => {
+                               use io::Read;
+                               let mut pubkey_read = core::slice::from_mut(&mut first_byte).chain(r.by_ref());
+                               IntroductionNode::NodeId(Readable::read(&mut pubkey_read)?)
+                       },
+                       _ => return Err(DecodeError::InvalidValue),
+               };
                let blinding_point = Readable::read(r)?;
                let num_hops: u8 = Readable::read(r)?;
                if num_hops == 0 { return Err(DecodeError::InvalidValue) }
@@ -150,7 +292,7 @@ impl Readable for BlindedPath {
                        blinded_hops.push(Readable::read(r)?);
                }
                Ok(BlindedPath {
-                       introduction_node_id,
+                       introduction_node,
                        blinding_point,
                        blinded_hops,
                })
@@ -162,3 +304,25 @@ impl_writeable!(BlindedHop, {
        encrypted_payload
 });
 
+impl Direction {
+       /// Returns the [`NodeId`] from the inputs corresponding to the direction.
+       pub fn select_node_id(&self, node_a: NodeId, node_b: NodeId) -> NodeId {
+               match self {
+                       Direction::NodeOne => core::cmp::min(node_a, node_b),
+                       Direction::NodeTwo => core::cmp::max(node_a, node_b),
+               }
+       }
+
+       /// Returns the [`PublicKey`] from the inputs corresponding to the direction.
+       pub fn select_pubkey<'a>(&self, node_a: &'a PublicKey, node_b: &'a PublicKey) -> &'a PublicKey {
+               let (node_one, node_two) = if NodeId::from_pubkey(node_a) < NodeId::from_pubkey(node_b) {
+                       (node_a, node_b)
+               } else {
+                       (node_b, node_a)
+               };
+               match self {
+                       Direction::NodeOne => node_one,
+                       Direction::NodeTwo => node_two,
+               }
+       }
+}
index 0ed09a366d8be7075415b92abad585a51fad6c9b..5e44c792d33b9c49df82c2d9840e462ad45cb9f6 100644 (file)
@@ -1,3 +1,12 @@
+// 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.
+
 //! Data structures and methods for constructing [`BlindedPath`]s to send a payment over.
 //!
 //! [`BlindedPath`]: crate::blinded_path::BlindedPath
@@ -7,15 +16,17 @@ use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
 use crate::blinded_path::BlindedHop;
 use crate::blinded_path::utils;
 use crate::io;
-use crate::ln::PaymentSecret;
+use crate::ln::types::PaymentSecret;
 use crate::ln::channelmanager::CounterpartyForwardingInfo;
 use crate::ln::features::BlindedHopFeatures;
 use crate::ln::msgs::DecodeError;
 use crate::offers::invoice::BlindedPayInfo;
-use crate::prelude::*;
+use crate::offers::invoice_request::InvoiceRequestFields;
+use crate::offers::offer::OfferId;
 use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, Writeable, Writer};
 
-use core::convert::TryFrom;
+#[allow(unused_imports)]
+use crate::prelude::*;
 
 /// An intermediate node, its outbound channel, and relay parameters.
 #[derive(Clone, Debug)]
@@ -53,6 +64,8 @@ pub struct ReceiveTlvs {
        pub payment_secret: PaymentSecret,
        /// Constraints for the receiver of this payment.
        pub payment_constraints: PaymentConstraints,
+       /// Context for the receiver of this payment.
+       pub payment_context: PaymentContext,
 }
 
 /// Data to construct a [`BlindedHop`] for sending a payment over.
@@ -97,6 +110,66 @@ pub struct PaymentConstraints {
        pub htlc_minimum_msat: u64,
 }
 
+/// The context of an inbound payment, which is included in a [`BlindedPath`] via [`ReceiveTlvs`]
+/// and surfaced in [`PaymentPurpose`].
+///
+/// [`BlindedPath`]: crate::blinded_path::BlindedPath
+/// [`PaymentPurpose`]: crate::events::PaymentPurpose
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum PaymentContext {
+       /// The payment context was unknown.
+       Unknown(UnknownPaymentContext),
+
+       /// The payment was made for an invoice requested from a BOLT 12 [`Offer`].
+       ///
+       /// [`Offer`]: crate::offers::offer::Offer
+       Bolt12Offer(Bolt12OfferContext),
+
+       /// The payment was made for an invoice sent for a BOLT 12 [`Refund`].
+       ///
+       /// [`Refund`]: crate::offers::refund::Refund
+       Bolt12Refund(Bolt12RefundContext),
+}
+
+// Used when writing PaymentContext in Event::PaymentClaimable to avoid cloning.
+pub(crate) enum PaymentContextRef<'a> {
+       Bolt12Offer(&'a Bolt12OfferContext),
+       Bolt12Refund(&'a Bolt12RefundContext),
+}
+
+/// An unknown payment context.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct UnknownPaymentContext(());
+
+/// The context of a payment made for an invoice requested from a BOLT 12 [`Offer`].
+///
+/// [`Offer`]: crate::offers::offer::Offer
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Bolt12OfferContext {
+       /// The identifier of the [`Offer`].
+       ///
+       /// [`Offer`]: crate::offers::offer::Offer
+       pub offer_id: OfferId,
+
+       /// Fields from an [`InvoiceRequest`] sent for a [`Bolt12Invoice`].
+       ///
+       /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+       /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
+       pub invoice_request: InvoiceRequestFields,
+}
+
+/// The context of a payment made for an invoice sent for a BOLT 12 [`Refund`].
+///
+/// [`Refund`]: crate::offers::refund::Refund
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Bolt12RefundContext {}
+
+impl PaymentContext {
+       pub(crate) fn unknown() -> Self {
+               PaymentContext::Unknown(UnknownPaymentContext(()))
+       }
+}
+
 impl TryFrom<CounterpartyForwardingInfo> for PaymentRelay {
        type Error = ();
 
@@ -120,11 +193,14 @@ impl TryFrom<CounterpartyForwardingInfo> for PaymentRelay {
 
 impl Writeable for ForwardTlvs {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               let features_opt =
+                       if self.features == BlindedHopFeatures::empty() { None }
+                       else { Some(&self.features) };
                encode_tlv_stream!(w, {
                        (2, self.short_channel_id, required),
                        (10, self.payment_relay, required),
                        (12, self.payment_constraints, required),
-                       (14, self.features, required)
+                       (14, features_opt, option)
                });
                Ok(())
        }
@@ -134,7 +210,8 @@ impl Writeable for ReceiveTlvs {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
                encode_tlv_stream!(w, {
                        (12, self.payment_constraints, required),
-                       (65536, self.payment_secret, required)
+                       (65536, self.payment_secret, required),
+                       (65537, self.payment_context, required)
                });
                Ok(())
        }
@@ -160,11 +237,14 @@ impl Readable for BlindedPaymentTlvs {
                        (12, payment_constraints, required),
                        (14, features, option),
                        (65536, payment_secret, option),
+                       (65537, payment_context, (default_value, PaymentContext::unknown())),
                });
                let _padding: Option<utils::Padding> = _padding;
 
                if let Some(short_channel_id) = scid {
-                       if payment_secret.is_some() { return Err(DecodeError::InvalidValue) }
+                       if payment_secret.is_some() {
+                               return Err(DecodeError::InvalidValue)
+                       }
                        Ok(BlindedPaymentTlvs::Forward(ForwardTlvs {
                                short_channel_id,
                                payment_relay: payment_relay.ok_or(DecodeError::InvalidValue)?,
@@ -176,6 +256,7 @@ impl Readable for BlindedPaymentTlvs {
                        Ok(BlindedPaymentTlvs::Receive(ReceiveTlvs {
                                payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?,
                                payment_constraints: payment_constraints.0.unwrap(),
+                               payment_context: payment_context.0.unwrap(),
                        }))
                }
        }
@@ -306,11 +387,54 @@ impl Readable for PaymentConstraints {
        }
 }
 
+impl_writeable_tlv_based_enum!(PaymentContext,
+       ;
+       (0, Unknown),
+       (1, Bolt12Offer),
+       (2, Bolt12Refund),
+);
+
+impl<'a> Writeable for PaymentContextRef<'a> {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               match self {
+                       PaymentContextRef::Bolt12Offer(context) => {
+                               1u8.write(w)?;
+                               context.write(w)?;
+                       },
+                       PaymentContextRef::Bolt12Refund(context) => {
+                               2u8.write(w)?;
+                               context.write(w)?;
+                       },
+               }
+
+               Ok(())
+       }
+}
+
+impl Writeable for UnknownPaymentContext {
+       fn write<W: Writer>(&self, _w: &mut W) -> Result<(), io::Error> {
+               Ok(())
+       }
+}
+
+impl Readable for UnknownPaymentContext {
+       fn read<R: io::Read>(_r: &mut R) -> Result<Self, DecodeError> {
+               Ok(UnknownPaymentContext(()))
+       }
+}
+
+impl_writeable_tlv_based!(Bolt12OfferContext, {
+       (0, offer_id, required),
+       (2, invoice_request, required),
+});
+
+impl_writeable_tlv_based!(Bolt12RefundContext, {});
+
 #[cfg(test)]
 mod tests {
        use bitcoin::secp256k1::PublicKey;
-       use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, ReceiveTlvs, PaymentConstraints, PaymentRelay};
-       use crate::ln::PaymentSecret;
+       use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, ReceiveTlvs, PaymentConstraints, PaymentContext, PaymentRelay};
+       use crate::ln::types::PaymentSecret;
        use crate::ln::features::BlindedHopFeatures;
        use crate::ln::functional_test_utils::TEST_FINAL_CLTV;
 
@@ -358,6 +482,7 @@ mod tests {
                                max_cltv_expiry: 0,
                                htlc_minimum_msat: 1,
                        },
+                       payment_context: PaymentContext::unknown(),
                };
                let htlc_maximum_msat = 100_000;
                let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, 12).unwrap();
@@ -376,6 +501,7 @@ mod tests {
                                max_cltv_expiry: 0,
                                htlc_minimum_msat: 1,
                        },
+                       payment_context: PaymentContext::unknown(),
                };
                let blinded_payinfo = super::compute_payinfo(&[], &recv_tlvs, 4242, TEST_FINAL_CLTV as u16).unwrap();
                assert_eq!(blinded_payinfo.fee_base_msat, 0);
@@ -429,6 +555,7 @@ mod tests {
                                max_cltv_expiry: 0,
                                htlc_minimum_msat: 3,
                        },
+                       payment_context: PaymentContext::unknown(),
                };
                let htlc_maximum_msat = 100_000;
                let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_maximum_msat, TEST_FINAL_CLTV as u16).unwrap();
@@ -479,6 +606,7 @@ mod tests {
                                max_cltv_expiry: 0,
                                htlc_minimum_msat: 1,
                        },
+                       payment_context: PaymentContext::unknown(),
                };
                let htlc_minimum_msat = 3798;
                assert!(super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, htlc_minimum_msat - 1, TEST_FINAL_CLTV as u16).is_err());
@@ -533,6 +661,7 @@ mod tests {
                                max_cltv_expiry: 0,
                                htlc_minimum_msat: 1,
                        },
+                       payment_context: PaymentContext::unknown(),
                };
 
                let blinded_payinfo = super::compute_payinfo(&intermediate_nodes[..], &recv_tlvs, 10_000, TEST_FINAL_CLTV as u16).unwrap();
index d4894a86aa1a9069abdc9b2e073cd0a31c789048..7e43f31453637a6fdea2309d4713f2b34567693f 100644 (file)
@@ -23,6 +23,8 @@ use crate::crypto::streams::ChaChaPolyWriteAdapter;
 use crate::util::ser::{Readable, Writeable};
 
 use crate::io;
+
+#[allow(unused_imports)]
 use crate::prelude::*;
 
 // TODO: DRY with onion_utils::construct_onion_keys_callback
index 1f42dc2fe4251a26be3c7b659bae89505893b6e3..9909e115ed610d63f10fdfab9ee8397ac1363641 100644 (file)
@@ -14,7 +14,8 @@
 //! disconnections, transaction broadcasting, and feerate information requests.
 
 use core::{cmp, ops::Deref};
-use core::convert::TryInto;
+
+use crate::prelude::*;
 
 use bitcoin::blockdata::transaction::Transaction;
 
@@ -123,6 +124,17 @@ pub enum ConfirmationTarget {
        ///
        /// [`ChannelManager::close_channel_with_feerate_and_script`]: crate::ln::channelmanager::ChannelManager::close_channel_with_feerate_and_script
        ChannelCloseMinimum,
+       /// The feerate [`OutputSweeper`] will use on transactions spending
+       /// [`SpendableOutputDescriptor`]s after a channel closure.
+       ///
+       /// Generally spending these outputs is safe as long as they eventually confirm, so a value
+       /// (slightly above) the mempool minimum should suffice. However, as this value will influence
+       /// how long funds will be unavailable after channel closure, [`FeeEstimator`] implementors
+       /// might want to choose a higher feerate to regain control over funds faster.
+       ///
+       /// [`OutputSweeper`]: crate::util::sweep::OutputSweeper
+       /// [`SpendableOutputDescriptor`]: crate::sign::SpendableOutputDescriptor
+       OutputSpendingFee,
 }
 
 /// A trait which should be implemented to provide feerate information on a number of time
@@ -135,6 +147,10 @@ pub enum ConfirmationTarget {
 ///
 /// Note that all of the functions implemented here *must* be reentrant-safe (obviously - they're
 /// called from inside the library in response to chain events, P2P events, or timer events).
+///
+/// LDK may generate a substantial number of fee-estimation calls in some cases. You should
+/// pre-calculate and cache the fee estimate results to ensure you don't substantially slow HTLC
+/// handling.
 pub trait FeeEstimator {
        /// Gets estimated satoshis of fee required per 1000 Weight-Units.
        ///
index 015b3dacfc3db6cc0e5a526729c6731d2c4c832f..04f72d4ce3bfa9c074551988b4a386fff66be0f6 100644 (file)
@@ -29,13 +29,12 @@ use bitcoin::hash_types::{Txid, BlockHash};
 use crate::chain;
 use crate::chain::{ChannelMonitorUpdateStatus, Filter, WatchedOutput};
 use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
-use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, Balance, MonitorEvent, TransactionOutputs, WithChannelMonitor, LATENCY_GRACE_PERIOD_BLOCKS};
+use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, Balance, MonitorEvent, TransactionOutputs, WithChannelMonitor};
 use crate::chain::transaction::{OutPoint, TransactionData};
-use crate::ln::ChannelId;
-use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
+use crate::ln::types::ChannelId;
+use crate::sign::ecdsa::EcdsaChannelSigner;
 use crate::events;
 use crate::events::{Event, EventHandler};
-use crate::util::atomic_counter::AtomicCounter;
 use crate::util::logger::{Logger, WithContext};
 use crate::util::errors::APIError;
 use crate::util::wakers::{Future, Notifier};
@@ -47,46 +46,6 @@ use core::ops::Deref;
 use core::sync::atomic::{AtomicUsize, Ordering};
 use bitcoin::secp256k1::PublicKey;
 
-mod update_origin {
-       #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
-       /// A specific update's ID stored in a `MonitorUpdateId`, separated out to make the contents
-       /// entirely opaque.
-       pub(crate) enum UpdateOrigin {
-               /// An update that was generated by the `ChannelManager` (via our [`crate::chain::Watch`]
-               /// implementation). This corresponds to an actual [ChannelMonitorUpdate::update_id] field
-               /// and [ChannelMonitor::get_latest_update_id].
-               ///
-               /// [ChannelMonitor::get_latest_update_id]: crate::chain::channelmonitor::ChannelMonitor::get_latest_update_id
-               /// [ChannelMonitorUpdate::update_id]: crate::chain::channelmonitor::ChannelMonitorUpdate::update_id
-               OffChain(u64),
-               /// An update that was generated during blockchain processing. The ID here is specific to the
-               /// generating [ChannelMonitor] and does *not* correspond to any on-disk IDs.
-               ///
-               /// [ChannelMonitor]: crate::chain::channelmonitor::ChannelMonitor
-               ChainSync(u64),
-       }
-}
-
-#[cfg(any(feature = "_test_utils", test))]
-pub(crate) use update_origin::UpdateOrigin;
-#[cfg(not(any(feature = "_test_utils", test)))]
-use update_origin::UpdateOrigin;
-
-/// An opaque identifier describing a specific [`Persist`] method call.
-#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
-pub struct MonitorUpdateId {
-       pub(crate) contents: UpdateOrigin,
-}
-
-impl MonitorUpdateId {
-       pub(crate) fn from_monitor_update(update: &ChannelMonitorUpdate) -> Self {
-               Self { contents: UpdateOrigin::OffChain(update.update_id) }
-       }
-       pub(crate) fn from_new_monitor<ChannelSigner: WriteableEcdsaChannelSigner>(monitor: &ChannelMonitor<ChannelSigner>) -> Self {
-               Self { contents: UpdateOrigin::OffChain(monitor.get_latest_update_id()) }
-       }
-}
-
 /// `Persist` defines behavior for persisting channel monitors: this could mean
 /// writing once to disk, and/or uploading to one or more backup services.
 ///
@@ -119,7 +78,7 @@ impl MonitorUpdateId {
 ///  All calls should generally spawn a background task and immediately return
 ///  [`ChannelMonitorUpdateStatus::InProgress`]. Once the update completes,
 ///  [`ChainMonitor::channel_monitor_updated`] should be called with the corresponding
-///  [`MonitorUpdateId`].
+///  [`ChannelMonitor::get_latest_update_id`] or [`ChannelMonitorUpdate::update_id`].
 ///
 ///  Note that unlike the direct [`chain::Watch`] interface,
 ///  [`ChainMonitor::channel_monitor_updated`] must be called once for *each* update which occurs.
@@ -142,7 +101,7 @@ impl MonitorUpdateId {
 ///
 /// [`TrustedCommitmentTransaction::revokeable_output_index`]: crate::ln::chan_utils::TrustedCommitmentTransaction::revokeable_output_index
 /// [`TrustedCommitmentTransaction::build_to_local_justice_tx`]: crate::ln::chan_utils::TrustedCommitmentTransaction::build_to_local_justice_tx
-pub trait Persist<ChannelSigner: WriteableEcdsaChannelSigner> {
+pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
        /// Persist a new channel's data in response to a [`chain::Watch::watch_channel`] call. This is
        /// called by [`ChannelManager`] for new channels, or may be called directly, e.g. on startup.
        ///
@@ -150,15 +109,16 @@ pub trait Persist<ChannelSigner: WriteableEcdsaChannelSigner> {
        /// channel's outpoint (and it is up to you to maintain a correct mapping between the outpoint
        /// and the stored channel data). Note that you **must** persist every new monitor to disk.
        ///
-       /// The `update_id` is used to identify this call to [`ChainMonitor::channel_monitor_updated`],
-       /// if you return [`ChannelMonitorUpdateStatus::InProgress`].
+       /// The [`ChannelMonitor::get_latest_update_id`] uniquely links this call to [`ChainMonitor::channel_monitor_updated`].
+       /// For [`Persist::persist_new_channel`], it is only necessary to call [`ChainMonitor::channel_monitor_updated`]
+       /// when you return [`ChannelMonitorUpdateStatus::InProgress`].
        ///
        /// See [`Writeable::write`] on [`ChannelMonitor`] for writing out a `ChannelMonitor`
        /// and [`ChannelMonitorUpdateStatus`] for requirements when returning errors.
        ///
        /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
        /// [`Writeable::write`]: crate::util::ser::Writeable::write
-       fn persist_new_channel(&self, channel_funding_outpoint: OutPoint, data: &ChannelMonitor<ChannelSigner>, update_id: MonitorUpdateId) -> ChannelMonitorUpdateStatus;
+       fn persist_new_channel(&self, channel_funding_outpoint: OutPoint, monitor: &ChannelMonitor<ChannelSigner>) -> ChannelMonitorUpdateStatus;
 
        /// Update one channel's data. The provided [`ChannelMonitor`] has already applied the given
        /// update.
@@ -185,18 +145,25 @@ pub trait Persist<ChannelSigner: WriteableEcdsaChannelSigner> {
        /// them in batches. The size of each monitor grows `O(number of state updates)`
        /// whereas updates are small and `O(1)`.
        ///
-       /// The `update_id` is used to identify this call to [`ChainMonitor::channel_monitor_updated`],
-       /// if you return [`ChannelMonitorUpdateStatus::InProgress`].
+       /// The [`ChannelMonitorUpdate::update_id`] or [`ChannelMonitor::get_latest_update_id`] uniquely
+       /// links this call to [`ChainMonitor::channel_monitor_updated`].
+       /// For [`Persist::update_persisted_channel`], it is only necessary to call [`ChainMonitor::channel_monitor_updated`]
+       /// when a [`ChannelMonitorUpdate`] is provided and when you return [`ChannelMonitorUpdateStatus::InProgress`].
        ///
        /// See [`Writeable::write`] on [`ChannelMonitor`] for writing out a `ChannelMonitor`,
        /// [`Writeable::write`] on [`ChannelMonitorUpdate`] for writing out an update, and
        /// [`ChannelMonitorUpdateStatus`] for requirements when returning errors.
        ///
        /// [`Writeable::write`]: crate::util::ser::Writeable::write
-       fn update_persisted_channel(&self, channel_funding_outpoint: OutPoint, update: Option<&ChannelMonitorUpdate>, data: &ChannelMonitor<ChannelSigner>, update_id: MonitorUpdateId) -> ChannelMonitorUpdateStatus;
+       fn update_persisted_channel(&self, channel_funding_outpoint: OutPoint, monitor_update: Option<&ChannelMonitorUpdate>, monitor: &ChannelMonitor<ChannelSigner>) -> ChannelMonitorUpdateStatus;
+       /// Prevents the channel monitor from being loaded on startup.
+       ///
+       /// Archiving the data in a backup location (rather than deleting it fully) is useful for
+       /// hedging against data loss in case of unexpected failure.
+       fn archive_persisted_channel(&self, channel_funding_outpoint: OutPoint);
 }
 
-struct MonitorHolder<ChannelSigner: WriteableEcdsaChannelSigner> {
+struct MonitorHolder<ChannelSigner: EcdsaChannelSigner> {
        monitor: ChannelMonitor<ChannelSigner>,
        /// The full set of pending monitor updates for this Channel.
        ///
@@ -204,35 +171,12 @@ struct MonitorHolder<ChannelSigner: WriteableEcdsaChannelSigner> {
        /// update_persisted_channel, the user returns a
        /// [`ChannelMonitorUpdateStatus::InProgress`], and then calls channel_monitor_updated
        /// immediately, racing our insertion of the pending update into the contained Vec.
-       ///
-       /// Beyond the synchronization of updates themselves, we cannot handle user events until after
-       /// any chain updates have been stored on disk. Thus, we scan this list when returning updates
-       /// to the ChannelManager, refusing to return any updates for a ChannelMonitor which is still
-       /// being persisted fully to disk after a chain update.
-       ///
-       /// This avoids the possibility of handling, e.g. an on-chain claim, generating a claim monitor
-       /// event, resulting in the relevant ChannelManager generating a PaymentSent event and dropping
-       /// the pending payment entry, and then reloading before the monitor is persisted, resulting in
-       /// the ChannelManager re-adding the same payment entry, before the same block is replayed,
-       /// resulting in a duplicate PaymentSent event.
-       pending_monitor_updates: Mutex<Vec<MonitorUpdateId>>,
-       /// The last block height at which no [`UpdateOrigin::ChainSync`] monitor updates were present
-       /// in `pending_monitor_updates`.
-       /// If it's been more than [`LATENCY_GRACE_PERIOD_BLOCKS`] since we started waiting on a chain
-       /// sync event, we let monitor events return to `ChannelManager` because we cannot hold them up
-       /// forever or we'll end up with HTLC preimages waiting to feed back into an upstream channel
-       /// forever, risking funds loss.
-       last_chain_persist_height: AtomicUsize,
+       pending_monitor_updates: Mutex<Vec<u64>>,
 }
 
-impl<ChannelSigner: WriteableEcdsaChannelSigner> MonitorHolder<ChannelSigner> {
-       fn has_pending_offchain_updates(&self, pending_monitor_updates_lock: &MutexGuard<Vec<MonitorUpdateId>>) -> bool {
-               pending_monitor_updates_lock.iter().any(|update_id|
-                       if let UpdateOrigin::OffChain(_) = update_id.contents { true } else { false })
-       }
-       fn has_pending_chainsync_updates(&self, pending_monitor_updates_lock: &MutexGuard<Vec<MonitorUpdateId>>) -> bool {
-               pending_monitor_updates_lock.iter().any(|update_id|
-                       if let UpdateOrigin::ChainSync(_) = update_id.contents { true } else { false })
+impl<ChannelSigner: EcdsaChannelSigner> MonitorHolder<ChannelSigner> {
+       fn has_pending_updates(&self, pending_monitor_updates_lock: &MutexGuard<Vec<u64>>) -> bool {
+               !pending_monitor_updates_lock.is_empty()
        }
 }
 
@@ -240,12 +184,12 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> MonitorHolder<ChannelSigner> {
 ///
 /// Note that this holds a mutex in [`ChainMonitor`] and may block other events until it is
 /// released.
-pub struct LockedChannelMonitor<'a, ChannelSigner: WriteableEcdsaChannelSigner> {
+pub struct LockedChannelMonitor<'a, ChannelSigner: EcdsaChannelSigner> {
        lock: RwLockReadGuard<'a, HashMap<OutPoint, MonitorHolder<ChannelSigner>>>,
        funding_txo: OutPoint,
 }
 
-impl<ChannelSigner: WriteableEcdsaChannelSigner> Deref for LockedChannelMonitor<'_, ChannelSigner> {
+impl<ChannelSigner: EcdsaChannelSigner> Deref for LockedChannelMonitor<'_, ChannelSigner> {
        type Target = ChannelMonitor<ChannelSigner>;
        fn deref(&self) -> &ChannelMonitor<ChannelSigner> {
                &self.lock.get(&self.funding_txo).expect("Checked at construction").monitor
@@ -268,7 +212,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> Deref for LockedChannelMonitor<
 /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
 /// [module-level documentation]: crate::chain::chainmonitor
 /// [`rebroadcast_pending_claims`]: Self::rebroadcast_pending_claims
-pub struct ChainMonitor<ChannelSigner: WriteableEcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref>
+pub struct ChainMonitor<ChannelSigner: EcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref>
        where C::Target: chain::Filter,
         T::Target: BroadcasterInterface,
         F::Target: FeeEstimator,
@@ -276,10 +220,6 @@ pub struct ChainMonitor<ChannelSigner: WriteableEcdsaChannelSigner, C: Deref, T:
         P::Target: Persist<ChannelSigner>,
 {
        monitors: RwLock<HashMap<OutPoint, MonitorHolder<ChannelSigner>>>,
-       /// When we generate a [`MonitorUpdateId`] for a chain-event monitor persistence, we need a
-       /// unique ID, which we calculate by simply getting the next value from this counter. Note that
-       /// the ID is never persisted so it's ok that they reset on restart.
-       sync_persistence_id: AtomicCounter,
        chain_source: Option<C>,
        broadcaster: T,
        logger: L,
@@ -291,10 +231,12 @@ pub struct ChainMonitor<ChannelSigner: WriteableEcdsaChannelSigner, C: Deref, T:
        /// The best block height seen, used as a proxy for the passage of time.
        highest_chain_height: AtomicUsize,
 
+       /// A [`Notifier`] used to wake up the background processor in case we have any [`Event`]s for
+       /// it to give to users (or [`MonitorEvent`]s for `ChannelManager` to process).
        event_notifier: Notifier,
 }
 
-impl<ChannelSigner: WriteableEcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref> ChainMonitor<ChannelSigner, C, T, F, L, P>
+impl<ChannelSigner: EcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref> ChainMonitor<ChannelSigner, C, T, F, L, P>
 where C::Target: chain::Filter,
            T::Target: BroadcasterInterface,
            F::Target: FeeEstimator,
@@ -321,7 +263,7 @@ where C::Target: chain::Filter,
                for funding_outpoint in funding_outpoints.iter() {
                        let monitor_lock = self.monitors.read().unwrap();
                        if let Some(monitor_state) = monitor_lock.get(funding_outpoint) {
-                               if self.update_monitor_with_chain_data(header, best_height, txdata, &process, funding_outpoint, &monitor_state).is_err() {
+                               if self.update_monitor_with_chain_data(header, txdata, &process, funding_outpoint, &monitor_state).is_err() {
                                        // Take the monitors lock for writing so that we poison it and any future
                                        // operations going forward fail immediately.
                                        core::mem::drop(monitor_lock);
@@ -336,7 +278,7 @@ where C::Target: chain::Filter,
                let monitor_states = self.monitors.write().unwrap();
                for (funding_outpoint, monitor_state) in monitor_states.iter() {
                        if !funding_outpoints.contains(funding_outpoint) {
-                               if self.update_monitor_with_chain_data(header, best_height, txdata, &process, funding_outpoint, &monitor_state).is_err() {
+                               if self.update_monitor_with_chain_data(header, txdata, &process, funding_outpoint, &monitor_state).is_err() {
                                        log_error!(self.logger, "{}", err_str);
                                        panic!("{}", err_str);
                                }
@@ -355,34 +297,22 @@ where C::Target: chain::Filter,
        }
 
        fn update_monitor_with_chain_data<FN>(
-               &self, header: &Header, best_height: Option<u32>, txdata: &TransactionData,
-               process: FN, funding_outpoint: &OutPoint, monitor_state: &MonitorHolder<ChannelSigner>
+               &self, header: &Header, txdata: &TransactionData, process: FN, funding_outpoint: &OutPoint,
+               monitor_state: &MonitorHolder<ChannelSigner>
        ) -> Result<(), ()> where FN: Fn(&ChannelMonitor<ChannelSigner>, &TransactionData) -> Vec<TransactionOutputs> {
                let monitor = &monitor_state.monitor;
-               let logger = WithChannelMonitor::from(&self.logger, &monitor);
+               let logger = WithChannelMonitor::from(&self.logger, &monitor, None);
                let mut txn_outputs;
                {
                        txn_outputs = process(monitor, txdata);
-                       let update_id = MonitorUpdateId {
-                               contents: UpdateOrigin::ChainSync(self.sync_persistence_id.get_increment()),
-                       };
-                       let mut pending_monitor_updates = monitor_state.pending_monitor_updates.lock().unwrap();
-                       if let Some(height) = best_height {
-                               if !monitor_state.has_pending_chainsync_updates(&pending_monitor_updates) {
-                                       // If there are not ChainSync persists awaiting completion, go ahead and
-                                       // set last_chain_persist_height here - we wouldn't want the first
-                                       // InProgress to always immediately be considered "overly delayed".
-                                       monitor_state.last_chain_persist_height.store(height as usize, Ordering::Release);
-                               }
-                       }
-
                        log_trace!(logger, "Syncing Channel Monitor for channel {}", log_funding_info!(monitor));
-                       match self.persister.update_persisted_channel(*funding_outpoint, None, monitor, update_id) {
+                       match self.persister.update_persisted_channel(*funding_outpoint, None, monitor) {
                                ChannelMonitorUpdateStatus::Completed =>
-                                       log_trace!(logger, "Finished syncing Channel Monitor for channel {}", log_funding_info!(monitor)),
+                                       log_trace!(logger, "Finished syncing Channel Monitor for channel {} for block-data",
+                                               log_funding_info!(monitor)
+                                       ),
                                ChannelMonitorUpdateStatus::InProgress => {
-                                       log_debug!(logger, "Channel Monitor sync for channel {} in progress, holding events until completion!", log_funding_info!(monitor));
-                                       pending_monitor_updates.push(update_id);
+                                       log_trace!(logger, "Channel Monitor sync for channel {} in progress.", log_funding_info!(monitor));
                                },
                                ChannelMonitorUpdateStatus::UnrecoverableError => {
                                        return Err(());
@@ -420,7 +350,6 @@ where C::Target: chain::Filter,
        pub fn new(chain_source: Option<C>, broadcaster: T, logger: L, feeest: F, persister: P) -> Self {
                Self {
                        monitors: RwLock::new(new_hash_map()),
-                       sync_persistence_id: AtomicCounter::new(),
                        chain_source,
                        broadcaster,
                        logger,
@@ -484,7 +413,10 @@ where C::Target: chain::Filter,
 
        #[cfg(not(c_bindings))]
        /// Lists the pending updates for each [`ChannelMonitor`] (by `OutPoint` being monitored).
-       pub fn list_pending_monitor_updates(&self) -> HashMap<OutPoint, Vec<MonitorUpdateId>> {
+       /// Each `Vec<u64>` contains `update_id`s from [`ChannelMonitor::get_latest_update_id`] for updates
+       /// that have not yet been fully persisted. Note that if a full monitor is persisted all the pending
+       /// monitor updates must be individually marked completed by calling [`ChainMonitor::channel_monitor_updated`].
+       pub fn list_pending_monitor_updates(&self) -> HashMap<OutPoint, Vec<u64>> {
                hash_map_from_iter(self.monitors.read().unwrap().iter().map(|(outpoint, holder)| {
                        (*outpoint, holder.pending_monitor_updates.lock().unwrap().clone())
                }))
@@ -492,7 +424,10 @@ where C::Target: chain::Filter,
 
        #[cfg(c_bindings)]
        /// Lists the pending updates for each [`ChannelMonitor`] (by `OutPoint` being monitored).
-       pub fn list_pending_monitor_updates(&self) -> Vec<(OutPoint, Vec<MonitorUpdateId>)> {
+       /// Each `Vec<u64>` contains `update_id`s from [`ChannelMonitor::get_latest_update_id`] for updates
+       /// that have not yet been fully persisted. Note that if a full monitor is persisted all the pending
+       /// monitor updates must be individually marked completed by calling [`ChainMonitor::channel_monitor_updated`].
+       pub fn list_pending_monitor_updates(&self) -> Vec<(OutPoint, Vec<u64>)> {
                self.monitors.read().unwrap().iter().map(|(outpoint, holder)| {
                        (*outpoint, holder.pending_monitor_updates.lock().unwrap().clone())
                }).collect()
@@ -511,16 +446,20 @@ where C::Target: chain::Filter,
        ///  1) This [`ChainMonitor`] calls [`Persist::update_persisted_channel`] which stores the
        ///     update to disk and begins updating any remote (e.g. watchtower/backup) copies,
        ///     returning [`ChannelMonitorUpdateStatus::InProgress`],
-       ///  2) once all remote copies are updated, you call this function with the
-       ///     `completed_update_id` that completed, and once all pending updates have completed the
-       ///     channel will be re-enabled.
-       //      Note that we re-enable only after `UpdateOrigin::OffChain` updates complete, we don't
-       //      care about `UpdateOrigin::ChainSync` updates for the channel state being updated. We
-       //      only care about `UpdateOrigin::ChainSync` for returning `MonitorEvent`s.
+       ///  2) once all remote copies are updated, you call this function with [`ChannelMonitor::get_latest_update_id`]
+       ///     or [`ChannelMonitorUpdate::update_id`] as the `completed_update_id`, and once all pending
+       ///     updates have completed the channel will be re-enabled.
+       ///
+       /// It is only necessary to call [`ChainMonitor::channel_monitor_updated`] when you return [`ChannelMonitorUpdateStatus::InProgress`]
+       /// from [`Persist`] and either:
+       ///   1. A new [`ChannelMonitor`] was added in [`Persist::persist_new_channel`], or
+       ///   2. A [`ChannelMonitorUpdate`] was provided as part of [`Persist::update_persisted_channel`].
+       /// Note that we don't care about calls to [`Persist::update_persisted_channel`] where no
+       /// [`ChannelMonitorUpdate`] was provided.
        ///
        /// Returns an [`APIError::APIMisuseError`] if `funding_txo` does not match any currently
        /// registered [`ChannelMonitor`]s.
-       pub fn channel_monitor_updated(&self, funding_txo: OutPoint, completed_update_id: MonitorUpdateId) -> Result<(), APIError> {
+       pub fn channel_monitor_updated(&self, funding_txo: OutPoint, completed_update_id: u64) -> Result<(), APIError> {
                let monitors = self.monitors.read().unwrap();
                let monitor_data = if let Some(mon) = monitors.get(&funding_txo) { mon } else {
                        return Err(APIError::APIMisuseError { err: format!("No ChannelMonitor matching funding outpoint {:?} found", funding_txo) });
@@ -528,37 +467,28 @@ where C::Target: chain::Filter,
                let mut pending_monitor_updates = monitor_data.pending_monitor_updates.lock().unwrap();
                pending_monitor_updates.retain(|update_id| *update_id != completed_update_id);
 
-               match completed_update_id {
-                       MonitorUpdateId { contents: UpdateOrigin::OffChain(_) } => {
-                               // Note that we only check for `UpdateOrigin::OffChain` failures here - if
-                               // we're being told that a `UpdateOrigin::OffChain` monitor update completed,
-                               // we only care about ensuring we don't tell the `ChannelManager` to restore
-                               // the channel to normal operation until all `UpdateOrigin::OffChain` updates
-                               // complete.
-                               // If there's some `UpdateOrigin::ChainSync` update still pending that's okay
-                               // - we can still update our channel state, just as long as we don't return
-                               // `MonitorEvent`s from the monitor back to the `ChannelManager` until they
-                               // complete.
-                               let monitor_is_pending_updates = monitor_data.has_pending_offchain_updates(&pending_monitor_updates);
-                               if monitor_is_pending_updates {
-                                       // If there are still monitor updates pending, we cannot yet construct a
-                                       // Completed event.
-                                       return Ok(());
-                               }
-                               let channel_id = monitor_data.monitor.channel_id();
-                               self.pending_monitor_events.lock().unwrap().push((funding_txo, channel_id, vec![MonitorEvent::Completed {
-                                       funding_txo, channel_id,
-                                       monitor_update_id: monitor_data.monitor.get_latest_update_id(),
-                               }], monitor_data.monitor.get_counterparty_node_id()));
-                       },
-                       MonitorUpdateId { contents: UpdateOrigin::ChainSync(_) } => {
-                               if !monitor_data.has_pending_chainsync_updates(&pending_monitor_updates) {
-                                       monitor_data.last_chain_persist_height.store(self.highest_chain_height.load(Ordering::Acquire), Ordering::Release);
-                                       // The next time release_pending_monitor_events is called, any events for this
-                                       // ChannelMonitor will be returned.
-                               }
-                       },
+               // Note that we only check for pending non-chainsync monitor updates and we don't track monitor
+               // updates resulting from chainsync in `pending_monitor_updates`.
+               let monitor_is_pending_updates = monitor_data.has_pending_updates(&pending_monitor_updates);
+               log_debug!(self.logger, "Completed off-chain monitor update {} for channel with funding outpoint {:?}, {}",
+                       completed_update_id,
+                       funding_txo,
+                       if monitor_is_pending_updates {
+                               "still have pending off-chain updates"
+                       } else {
+                               "all off-chain updates complete, returning a MonitorEvent"
+                       });
+               if monitor_is_pending_updates {
+                       // If there are still monitor updates pending, we cannot yet construct a
+                       // Completed event.
+                       return Ok(());
                }
+               let channel_id = monitor_data.monitor.channel_id();
+               self.pending_monitor_events.lock().unwrap().push((funding_txo, channel_id, vec![MonitorEvent::Completed {
+                       funding_txo, channel_id,
+                       monitor_update_id: monitor_data.monitor.get_latest_update_id(),
+               }], monitor_data.monitor.get_counterparty_node_id()));
+
                self.event_notifier.notify();
                Ok(())
        }
@@ -656,9 +586,44 @@ where C::Target: chain::Filter,
                        }
                }
        }
+
+       /// Archives fully resolved channel monitors by calling [`Persist::archive_persisted_channel`].
+       ///
+       /// This is useful for pruning fully resolved monitors from the monitor set and primary
+       /// storage so they are not kept in memory and reloaded on restart.
+       ///
+       /// Should be called occasionally (once every handful of blocks or on startup).
+       ///
+       /// Depending on the implementation of [`Persist::archive_persisted_channel`] the monitor
+       /// data could be moved to an archive location or removed entirely.
+       pub fn archive_fully_resolved_channel_monitors(&self) {
+               let mut have_monitors_to_prune = false;
+               for (_, monitor_holder) in self.monitors.read().unwrap().iter() {
+                       let logger = WithChannelMonitor::from(&self.logger, &monitor_holder.monitor, None);
+                       if monitor_holder.monitor.is_fully_resolved(&logger) {
+                               have_monitors_to_prune = true;
+                       }
+               }
+               if have_monitors_to_prune {
+                       let mut monitors = self.monitors.write().unwrap();
+                       monitors.retain(|funding_txo, monitor_holder| {
+                               let logger = WithChannelMonitor::from(&self.logger, &monitor_holder.monitor, None);
+                               if monitor_holder.monitor.is_fully_resolved(&logger) {
+                                       log_info!(logger,
+                                               "Archiving fully resolved ChannelMonitor for funding txo {}",
+                                               funding_txo
+                                       );
+                                       self.persister.archive_persisted_channel(*funding_txo);
+                                       false
+                               } else {
+                                       true
+                               }
+                       });
+               }
+       }
 }
 
-impl<ChannelSigner: WriteableEcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref>
+impl<ChannelSigner: EcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref>
 chain::Listen for ChainMonitor<ChannelSigner, C, T, F, L, P>
 where
        C::Target: chain::Filter,
@@ -673,6 +638,8 @@ where
                        monitor.block_connected(
                                header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &self.logger)
                });
+               // Assume we may have some new events and wake the event processor
+               self.event_notifier.notify();
        }
 
        fn block_disconnected(&self, header: &Header, height: u32) {
@@ -685,7 +652,7 @@ where
        }
 }
 
-impl<ChannelSigner: WriteableEcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref>
+impl<ChannelSigner: EcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref>
 chain::Confirm for ChainMonitor<ChannelSigner, C, T, F, L, P>
 where
        C::Target: chain::Filter,
@@ -700,6 +667,8 @@ where
                        monitor.transactions_confirmed(
                                header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &self.logger)
                });
+               // Assume we may have some new events and wake the event processor
+               self.event_notifier.notify();
        }
 
        fn transaction_unconfirmed(&self, txid: &Txid) {
@@ -720,6 +689,8 @@ where
                                header, height, &*self.broadcaster, &*self.fee_estimator, &self.logger
                        )
                });
+               // Assume we may have some new events and wake the event processor
+               self.event_notifier.notify();
        }
 
        fn get_relevant_txids(&self) -> Vec<(Txid, u32, Option<BlockHash>)> {
@@ -735,7 +706,7 @@ where
        }
 }
 
-impl<ChannelSigner: WriteableEcdsaChannelSigner, C: Deref , T: Deref , F: Deref , L: Deref , P: Deref >
+impl<ChannelSigner: EcdsaChannelSigner, C: Deref , T: Deref , F: Deref , L: Deref , P: Deref >
 chain::Watch<ChannelSigner> for ChainMonitor<ChannelSigner, C, T, F, L, P>
 where C::Target: chain::Filter,
            T::Target: BroadcasterInterface,
@@ -744,7 +715,7 @@ where C::Target: chain::Filter,
            P::Target: Persist<ChannelSigner>,
 {
        fn watch_channel(&self, funding_outpoint: OutPoint, monitor: ChannelMonitor<ChannelSigner>) -> Result<ChannelMonitorUpdateStatus, ()> {
-               let logger = WithChannelMonitor::from(&self.logger, &monitor);
+               let logger = WithChannelMonitor::from(&self.logger, &monitor, None);
                let mut monitors = self.monitors.write().unwrap();
                let entry = match monitors.entry(funding_outpoint) {
                        hash_map::Entry::Occupied(_) => {
@@ -754,9 +725,9 @@ where C::Target: chain::Filter,
                        hash_map::Entry::Vacant(e) => e,
                };
                log_trace!(logger, "Got new ChannelMonitor for channel {}", log_funding_info!(monitor));
-               let update_id = MonitorUpdateId::from_new_monitor(&monitor);
+               let update_id = monitor.get_latest_update_id();
                let mut pending_monitor_updates = Vec::new();
-               let persist_res = self.persister.persist_new_channel(funding_outpoint, &monitor, update_id);
+               let persist_res = self.persister.persist_new_channel(funding_outpoint, &monitor);
                match persist_res {
                        ChannelMonitorUpdateStatus::InProgress => {
                                log_info!(logger, "Persistence of new ChannelMonitor for channel {} in progress", log_funding_info!(monitor));
@@ -777,7 +748,6 @@ where C::Target: chain::Filter,
                entry.insert(MonitorHolder {
                        monitor,
                        pending_monitor_updates: Mutex::new(pending_monitor_updates),
-                       last_chain_persist_height: AtomicUsize::new(self.highest_chain_height.load(Ordering::Acquire)),
                });
                Ok(persist_res)
        }
@@ -790,7 +760,7 @@ where C::Target: chain::Filter,
                let monitors = self.monitors.read().unwrap();
                match monitors.get(&funding_txo) {
                        None => {
-                               let logger = WithContext::from(&self.logger, update.counterparty_node_id, Some(channel_id));
+                               let logger = WithContext::from(&self.logger, update.counterparty_node_id, Some(channel_id), None);
                                log_error!(logger, "Failed to update channel monitor: no such monitor registered");
 
                                // We should never ever trigger this from within ChannelManager. Technically a
@@ -803,11 +773,11 @@ where C::Target: chain::Filter,
                        },
                        Some(monitor_state) => {
                                let monitor = &monitor_state.monitor;
-                               let logger = WithChannelMonitor::from(&self.logger, &monitor);
-                               log_trace!(logger, "Updating ChannelMonitor for channel {}", log_funding_info!(monitor));
+                               let logger = WithChannelMonitor::from(&self.logger, &monitor, None);
+                               log_trace!(logger, "Updating ChannelMonitor to id {} for channel {}", update.update_id, log_funding_info!(monitor));
                                let update_res = monitor.update_monitor(update, &self.broadcaster, &self.fee_estimator, &self.logger);
 
-                               let update_id = MonitorUpdateId::from_monitor_update(update);
+                               let update_id = update.update_id;
                                let mut pending_monitor_updates = monitor_state.pending_monitor_updates.lock().unwrap();
                                let persist_res = if update_res.is_err() {
                                        // Even if updating the monitor returns an error, the monitor's state will
@@ -816,17 +786,25 @@ where C::Target: chain::Filter,
                                        // while reading `channel_monitor` with updates from storage. Instead, we should persist
                                        // the entire `channel_monitor` here.
                                        log_warn!(logger, "Failed to update ChannelMonitor for channel {}. Going ahead and persisting the entire ChannelMonitor", log_funding_info!(monitor));
-                                       self.persister.update_persisted_channel(funding_txo, None, monitor, update_id)
+                                       self.persister.update_persisted_channel(funding_txo, None, monitor)
                                } else {
-                                       self.persister.update_persisted_channel(funding_txo, Some(update), monitor, update_id)
+                                       self.persister.update_persisted_channel(funding_txo, Some(update), monitor)
                                };
                                match persist_res {
                                        ChannelMonitorUpdateStatus::InProgress => {
                                                pending_monitor_updates.push(update_id);
-                                               log_debug!(logger, "Persistence of ChannelMonitorUpdate for channel {} in progress", log_funding_info!(monitor));
+                                               log_debug!(logger,
+                                                       "Persistence of ChannelMonitorUpdate id {:?} for channel {} in progress",
+                                                       update_id,
+                                                       log_funding_info!(monitor)
+                                               );
                                        },
                                        ChannelMonitorUpdateStatus::Completed => {
-                                               log_debug!(logger, "Persistence of ChannelMonitorUpdate for channel {} completed", log_funding_info!(monitor));
+                                               log_debug!(logger,
+                                                       "Persistence of ChannelMonitorUpdate id {:?} for channel {} completed",
+                                                       update_id,
+                                                       log_funding_info!(monitor)
+                                               );
                                        },
                                        ChannelMonitorUpdateStatus::UnrecoverableError => {
                                                // Take the monitors lock for writing so that we poison it and any future
@@ -851,28 +829,19 @@ where C::Target: chain::Filter,
        fn release_pending_monitor_events(&self) -> Vec<(OutPoint, ChannelId, Vec<MonitorEvent>, Option<PublicKey>)> {
                let mut pending_monitor_events = self.pending_monitor_events.lock().unwrap().split_off(0);
                for monitor_state in self.monitors.read().unwrap().values() {
-                       let logger = WithChannelMonitor::from(&self.logger, &monitor_state.monitor);
-                       let is_pending_monitor_update = monitor_state.has_pending_chainsync_updates(&monitor_state.pending_monitor_updates.lock().unwrap());
-                       if !is_pending_monitor_update || monitor_state.last_chain_persist_height.load(Ordering::Acquire) + LATENCY_GRACE_PERIOD_BLOCKS as usize <= self.highest_chain_height.load(Ordering::Acquire) {
-                               if is_pending_monitor_update {
-                                       log_error!(logger, "A ChannelMonitor sync took longer than {} blocks to complete.", LATENCY_GRACE_PERIOD_BLOCKS);
-                                       log_error!(logger, "   To avoid funds-loss, we are allowing monitor updates to be released.");
-                                       log_error!(logger, "   This may cause duplicate payment events to be generated.");
-                               }
-                               let monitor_events = monitor_state.monitor.get_and_clear_pending_monitor_events();
-                               if monitor_events.len() > 0 {
-                                       let monitor_outpoint = monitor_state.monitor.get_funding_txo().0;
-                                       let monitor_channel_id = monitor_state.monitor.channel_id();
-                                       let counterparty_node_id = monitor_state.monitor.get_counterparty_node_id();
-                                       pending_monitor_events.push((monitor_outpoint, monitor_channel_id, monitor_events, counterparty_node_id));
-                               }
+                       let monitor_events = monitor_state.monitor.get_and_clear_pending_monitor_events();
+                       if monitor_events.len() > 0 {
+                               let monitor_outpoint = monitor_state.monitor.get_funding_txo().0;
+                               let monitor_channel_id = monitor_state.monitor.channel_id();
+                               let counterparty_node_id = monitor_state.monitor.get_counterparty_node_id();
+                               pending_monitor_events.push((monitor_outpoint, monitor_channel_id, monitor_events, counterparty_node_id));
                        }
                }
                pending_monitor_events
        }
 }
 
-impl<ChannelSigner: WriteableEcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref> events::EventsProvider for ChainMonitor<ChannelSigner, C, T, F, L, P>
+impl<ChannelSigner: EcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref> events::EventsProvider for ChainMonitor<ChannelSigner, C, T, F, L, P>
        where C::Target: chain::Filter,
              T::Target: BroadcasterInterface,
              F::Target: FeeEstimator,
@@ -902,15 +871,12 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L
 #[cfg(test)]
 mod tests {
        use crate::check_added_monitors;
-       use crate::{expect_payment_claimed, 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};
-       use crate::chain::{ChannelMonitorUpdateStatus, Confirm, Watch};
-       use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS;
+       use crate::{expect_payment_path_successful, get_event_msg};
+       use crate::{get_htlc_update_msgs, get_revoke_commit_msgs};
+       use crate::chain::{ChannelMonitorUpdateStatus, Watch};
        use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider};
-       use crate::ln::channelmanager::{PaymentSendFailure, PaymentId, RecipientOnionFields};
        use crate::ln::functional_test_utils::*;
        use crate::ln::msgs::ChannelMessageHandler;
-       use crate::util::errors::APIError;
 
        #[test]
        fn test_async_ooo_offchain_updates() {
@@ -1017,76 +983,6 @@ mod tests {
                check_added_monitors!(nodes[0], 1);
        }
 
-       fn do_chainsync_pauses_events(block_timeout: bool) {
-               // When a chainsync monitor update occurs, any MonitorUpdates should be held before being
-               // passed upstream to a `ChannelManager` via `Watch::release_pending_monitor_events`. This
-               // tests that behavior, as well as some ways it might go wrong.
-               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 channel = create_announced_chan_between_nodes(&nodes, 0, 1);
-
-               // Get a route for later and rebalance the channel somewhat
-               send_payment(&nodes[0], &[&nodes[1]], 10_000_000);
-               let (route, second_payment_hash, _, second_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 100_000);
-
-               // First route a payment that we will claim on chain and give the recipient the preimage.
-               let (payment_preimage, payment_hash, ..) = route_payment(&nodes[0], &[&nodes[1]], 1_000_000);
-               nodes[1].node.claim_funds(payment_preimage);
-               expect_payment_claimed!(nodes[1], payment_hash, 1_000_000);
-               nodes[1].node.get_and_clear_pending_msg_events();
-               check_added_monitors!(nodes[1], 1);
-               let remote_txn = get_local_commitment_txn!(nodes[1], channel.2);
-               assert_eq!(remote_txn.len(), 2);
-
-               // Temp-fail the block connection which will hold the channel-closed event
-               chanmon_cfgs[0].persister.chain_sync_monitor_persistences.lock().unwrap().clear();
-               chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
-
-               // 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 = 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());
-               nodes[0].chain_monitor.chain_monitor.best_block_updated(&new_header, nodes[0].best_block_info().1 + 1);
-               assert!(nodes[0].chain_monitor.release_pending_monitor_events().is_empty());
-
-               // If the ChannelManager tries to update the channel, however, the ChainMonitor will pass
-               // the update through to the ChannelMonitor which will refuse it (as the channel is closed).
-               chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed);
-               unwrap_send_err!(nodes[0].node.send_payment_with_route(&route, second_payment_hash,
-                               RecipientOnionFields::secret_only(second_payment_secret), PaymentId(second_payment_hash.0)
-                       ), false, APIError::MonitorUpdateInProgress, {});
-               check_added_monitors!(nodes[0], 1);
-
-               // However, as the ChainMonitor is still waiting for the original persistence to complete,
-               // it won't yet release the MonitorEvents.
-               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 = 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();
-                       for (funding_outpoint, update_ids) in persistences {
-                               for update_id in update_ids {
-                                       nodes[0].chain_monitor.chain_monitor.channel_monitor_updated(funding_outpoint, update_id).unwrap();
-                               }
-                       }
-               }
-
-               expect_payment_sent(&nodes[0], payment_preimage, None, true, false);
-       }
-
-       #[test]
-       fn chainsync_pauses_events() {
-               do_chainsync_pauses_events(false);
-               do_chainsync_pauses_events(true);
-       }
-
        #[test]
        #[cfg(feature = "std")]
        fn update_during_chainsync_poisons_channel() {
@@ -1096,7 +992,6 @@ mod tests {
                let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
                create_announced_chan_between_nodes(&nodes, 0, 1);
 
-               chanmon_cfgs[0].persister.chain_sync_monitor_persistences.lock().unwrap().clear();
                chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::UnrecoverableError);
 
                assert!(std::panic::catch_unwind(|| {
@@ -1109,3 +1004,4 @@ mod tests {
                }).is_err());
        }
 }
+
index 5c81a513713f2d79d1b18a42dcc84789f9016f40..29f7130fbdeaca2e807e09cba9a8e86e9537f1db 100644 (file)
@@ -20,6 +20,7 @@
 //! security-domain-separated system design, you should consider having multiple paths for
 //! ChannelMonitors to get out of the HSM and onto monitoring devices.
 
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::block::Header;
 use bitcoin::blockdata::transaction::{OutPoint as BitcoinOutPoint, TxOut, Transaction};
 use bitcoin::blockdata::script::{Script, ScriptBuf};
@@ -28,13 +29,13 @@ use bitcoin::hashes::Hash;
 use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::hash_types::{Txid, BlockHash};
 
+use bitcoin::ecdsa::Signature as BitcoinSignature;
 use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
 use bitcoin::secp256k1::{SecretKey, PublicKey};
 use bitcoin::secp256k1;
-use bitcoin::sighash::EcdsaSighashType;
 
 use crate::ln::channel::INITIAL_COMMITMENT_NUMBER;
-use crate::ln::{PaymentHash, PaymentPreimage, ChannelId};
+use crate::ln::types::{PaymentHash, PaymentPreimage, ChannelId};
 use crate::ln::msgs::DecodeError;
 use crate::ln::channel_keys::{DelayedPaymentKey, DelayedPaymentBasepoint, HtlcBasepoint, HtlcKey, RevocationKey, RevocationBasepoint};
 use crate::ln::chan_utils::{self,CommitmentTransaction, CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCClaim, ChannelTransactionParameters, HolderCommitmentTransaction, TxCreationKeys};
@@ -43,20 +44,21 @@ use crate::chain;
 use crate::chain::{BestBlock, WatchedOutput};
 use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator, LowerBoundedFeeEstimator};
 use crate::chain::transaction::{OutPoint, TransactionData};
-use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, ecdsa::WriteableEcdsaChannelSigner, SignerProvider, EntropySource};
+use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, ecdsa::EcdsaChannelSigner, SignerProvider, EntropySource};
 use crate::chain::onchaintx::{ClaimEvent, FeerateStrategy, OnchainTxHandler};
 use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData, PackageTemplate, RevokedOutput, RevokedHTLCOutput};
 use crate::chain::Filter;
 use crate::util::logger::{Logger, Record};
 use crate::util::ser::{Readable, ReadableArgs, RequiredWrapper, MaybeReadable, UpgradableRequired, Writer, Writeable, U48};
 use crate::util::byte_utils;
-use crate::events::{Event, EventHandler};
+use crate::events::{ClosureReason, Event, EventHandler};
 use crate::events::bump_transaction::{AnchorDescriptor, BumpTransactionEvent};
 
+#[allow(unused_imports)]
 use crate::prelude::*;
+
 use core::{cmp, mem};
 use crate::io::{self, Error};
-use core::convert::TryInto;
 use core::ops::Deref;
 use crate::sync::{Mutex, LockTestExt};
 
@@ -155,6 +157,17 @@ pub enum MonitorEvent {
        /// A monitor event containing an HTLCUpdate.
        HTLCEvent(HTLCUpdate),
 
+       /// Indicates we broadcasted the channel's latest commitment transaction and thus closed the
+       /// channel. Holds information about the channel and why it was closed.
+       HolderForceClosedWithInfo {
+               /// The reason the channel was closed.
+               reason: ClosureReason,
+               /// The funding outpoint of the channel.
+               outpoint: OutPoint,
+               /// The channel ID of the channel.
+               channel_id: ChannelId,
+       },
+
        /// Indicates we broadcasted the channel's latest commitment transaction and thus closed the
        /// channel.
        HolderForceClosed(OutPoint),
@@ -184,6 +197,11 @@ impl_writeable_tlv_based_enum_upgradable!(MonitorEvent,
                (2, monitor_update_id, required),
                (4, channel_id, required),
        },
+       (5, HolderForceClosedWithInfo) => {
+               (0, reason, upgradable_required),
+               (2, outpoint, required),
+               (4, channel_id, required),
+       },
 ;
        (2, HTLCEvent),
        (4, HolderForceClosed),
@@ -394,7 +412,7 @@ impl OnchainEventEntry {
 /// The (output index, sats value) for the counterparty's output in a commitment transaction.
 ///
 /// This was added as an `Option` in 0.0.110.
-type CommitmentTxCounterpartyOutputInfo = Option<(u32, u64)>;
+type CommitmentTxCounterpartyOutputInfo = Option<(u32, Amount)>;
 
 /// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it
 /// once they mature to enough confirmations (ANTI_REORG_DELAY)
@@ -757,14 +775,14 @@ impl Readable for IrrevocablyResolvedHTLC {
 /// the "reorg path" (ie disconnecting blocks until you find a common ancestor from both the
 /// returned block hash and the the current chain and then reconnecting blocks to get to the
 /// best chain) upon deserializing the object!
-pub struct ChannelMonitor<Signer: WriteableEcdsaChannelSigner> {
+pub struct ChannelMonitor<Signer: EcdsaChannelSigner> {
        #[cfg(test)]
        pub(crate) inner: Mutex<ChannelMonitorImpl<Signer>>,
        #[cfg(not(test))]
        pub(super) inner: Mutex<ChannelMonitorImpl<Signer>>,
 }
 
-impl<Signer: WriteableEcdsaChannelSigner> Clone for ChannelMonitor<Signer> where Signer: Clone {
+impl<Signer: EcdsaChannelSigner> Clone for ChannelMonitor<Signer> where Signer: Clone {
        fn clone(&self) -> Self {
                let inner = self.inner.lock().unwrap().clone();
                ChannelMonitor::from_impl(inner)
@@ -772,7 +790,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Clone for ChannelMonitor<Signer> where
 }
 
 #[derive(Clone, PartialEq)]
-pub(crate) struct ChannelMonitorImpl<Signer: WriteableEcdsaChannelSigner> {
+pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
        latest_update_id: u64,
        commitment_transaction_number_obscure_factor: u64,
 
@@ -918,12 +936,15 @@ pub(crate) struct ChannelMonitorImpl<Signer: WriteableEcdsaChannelSigner> {
        /// Ordering of tuple data: (their_per_commitment_point, feerate_per_kw, to_broadcaster_sats,
        /// to_countersignatory_sats)
        initial_counterparty_commitment_info: Option<(PublicKey, u32, u64, u64)>,
+
+       /// The first block height at which we had no remaining claimable balances.
+       balances_empty_height: Option<u32>,
 }
 
 /// Transaction outputs to watch for on-chain spends.
 pub type TransactionOutputs = (Txid, Vec<(u32, TxOut)>);
 
-impl<Signer: WriteableEcdsaChannelSigner> PartialEq for ChannelMonitor<Signer> where Signer: PartialEq {
+impl<Signer: EcdsaChannelSigner> PartialEq for ChannelMonitor<Signer> where Signer: PartialEq {
        fn eq(&self, other: &Self) -> bool {
                // We need some kind of total lockorder. Absent a better idea, we sort by position in
                // memory and take locks in that order (assuming that we can't move within memory while a
@@ -935,7 +956,7 @@ impl<Signer: WriteableEcdsaChannelSigner> PartialEq for ChannelMonitor<Signer> w
        }
 }
 
-impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitor<Signer> {
+impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitor<Signer> {
        fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
                self.inner.lock().unwrap().write(writer)
        }
@@ -945,7 +966,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitor<Signer> {
 const SERIALIZATION_VERSION: u8 = 1;
 const MIN_SERIALIZATION_VERSION: u8 = 1;
 
-impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
+impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
        fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
                write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
 
@@ -1059,6 +1080,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signe
                writer.write_all(&(self.pending_monitor_events.iter().filter(|ev| match ev {
                        MonitorEvent::HTLCEvent(_) => true,
                        MonitorEvent::HolderForceClosed(_) => true,
+                       MonitorEvent::HolderForceClosedWithInfo { .. } => true,
                        _ => false,
                }).count() as u64).to_be_bytes())?;
                for event in self.pending_monitor_events.iter() {
@@ -1068,6 +1090,10 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signe
                                        upd.write(writer)?;
                                },
                                MonitorEvent::HolderForceClosed(_) => 1u8.write(writer)?,
+                               // `HolderForceClosedWithInfo` replaced `HolderForceClosed` in v0.0.122. To keep
+                               // backwards compatibility, we write a `HolderForceClosed` event along with the
+                               // `HolderForceClosedWithInfo` event. This is deduplicated in the reader.
+                               MonitorEvent::HolderForceClosedWithInfo { .. } => 1u8.write(writer)?,
                                _ => {}, // Covered in the TLV writes below
                        }
                }
@@ -1099,10 +1125,23 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signe
                self.lockdown_from_offchain.write(writer)?;
                self.holder_tx_signed.write(writer)?;
 
+               // If we have a `HolderForceClosedWithInfo` event, we need to write the `HolderForceClosed` for backwards compatibility.
+               let pending_monitor_events = match self.pending_monitor_events.iter().find(|ev| match ev {
+                       MonitorEvent::HolderForceClosedWithInfo { .. } => true,
+                       _ => false,
+               }) {
+                       Some(MonitorEvent::HolderForceClosedWithInfo { outpoint, .. }) => {
+                               let mut pending_monitor_events = self.pending_monitor_events.clone();
+                               pending_monitor_events.push(MonitorEvent::HolderForceClosed(*outpoint));
+                               pending_monitor_events
+                       }
+                       _ => self.pending_monitor_events.clone(),
+               };
+
                write_tlv_fields!(writer, {
                        (1, self.funding_spend_confirmed, option),
                        (3, self.htlcs_resolved_on_chain, required_vec),
-                       (5, self.pending_monitor_events, required_vec),
+                       (5, pending_monitor_events, required_vec),
                        (7, self.funding_spend_seen, required),
                        (9, self.counterparty_node_id, option),
                        (11, self.confirmed_commitment_tx_counterparty_output, option),
@@ -1110,6 +1149,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signe
                        (15, self.counterparty_fulfilled_htlcs, required),
                        (17, self.initial_counterparty_commitment_info, option),
                        (19, self.channel_id, required),
+                       (21, self.balances_empty_height, option),
                });
 
                Ok(())
@@ -1156,31 +1196,33 @@ pub(crate) struct WithChannelMonitor<'a, L: Deref> where L::Target: Logger {
        logger: &'a L,
        peer_id: Option<PublicKey>,
        channel_id: Option<ChannelId>,
+       payment_hash: Option<PaymentHash>,
 }
 
 impl<'a, L: Deref> Logger for WithChannelMonitor<'a, L> where L::Target: Logger {
        fn log(&self, mut record: Record) {
                record.peer_id = self.peer_id;
                record.channel_id = self.channel_id;
+               record.payment_hash = self.payment_hash;
                self.logger.log(record)
        }
 }
 
 impl<'a, L: Deref> WithChannelMonitor<'a, L> where L::Target: Logger {
-       pub(crate) fn from<S: WriteableEcdsaChannelSigner>(logger: &'a L, monitor: &ChannelMonitor<S>) -> Self {
-               Self::from_impl(logger, &*monitor.inner.lock().unwrap())
+       pub(crate) fn from<S: EcdsaChannelSigner>(logger: &'a L, monitor: &ChannelMonitor<S>, payment_hash: Option<PaymentHash>) -> Self {
+               Self::from_impl(logger, &*monitor.inner.lock().unwrap(), payment_hash)
        }
 
-       pub(crate) fn from_impl<S: WriteableEcdsaChannelSigner>(logger: &'a L, monitor_impl: &ChannelMonitorImpl<S>) -> Self {
+       pub(crate) fn from_impl<S: EcdsaChannelSigner>(logger: &'a L, monitor_impl: &ChannelMonitorImpl<S>, payment_hash: Option<PaymentHash>) -> Self {
                let peer_id = monitor_impl.counterparty_node_id;
                let channel_id = Some(monitor_impl.channel_id());
                WithChannelMonitor {
-                       logger, peer_id, channel_id,
+                       logger, peer_id, channel_id, payment_hash,
                }
        }
 }
 
-impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
+impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
        /// For lockorder enforcement purposes, we need to have a single site which constructs the
        /// `inner` mutex, otherwise cases where we lock two monitors at the same time (eg in our
        /// PartialEq implementation) we may decide a lockorder violation has occurred.
@@ -1293,6 +1335,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                        best_block,
                        counterparty_node_id: Some(counterparty_node_id),
                        initial_counterparty_commitment_info: None,
+                       balances_empty_height: None,
                })
        }
 
@@ -1316,7 +1359,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
        where L::Target: Logger
        {
                let mut inner = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
                inner.provide_initial_counterparty_commitment_tx(txid,
                        htlc_outputs, commitment_number, their_cur_per_commitment_point, feerate_per_kw,
                        to_broadcaster_value_sat, to_countersignatory_value_sat, &logger);
@@ -1336,7 +1379,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                logger: &L,
        ) where L::Target: Logger {
                let mut inner = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
                inner.provide_latest_counterparty_commitment_tx(
                        txid, htlc_outputs, commitment_number, their_per_commitment_point, &logger)
        }
@@ -1364,7 +1407,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                L::Target: Logger,
        {
                let mut inner = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, Some(*payment_hash));
                inner.provide_payment_preimage(
                        payment_hash, payment_preimage, broadcaster, fee_estimator, &logger)
        }
@@ -1386,7 +1429,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                L::Target: Logger,
        {
                let mut inner = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
                inner.update_monitor(updates, broadcaster, fee_estimator, &logger)
        }
 
@@ -1421,7 +1464,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                F::Target: chain::Filter, L::Target: Logger,
        {
                let lock = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*lock);
+               let logger = WithChannelMonitor::from_impl(logger, &*lock, None);
                log_trace!(&logger, "Registering funding outpoint {}", &lock.get_funding_txo().0);
                filter.register_tx(&lock.get_funding_txo().0.txid, &lock.get_funding_txo().1);
                for (txid, outputs) in lock.get_outputs_to_watch().iter() {
@@ -1581,7 +1624,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
        {
                let mut inner = self.inner.lock().unwrap();
                let fee_estimator = LowerBoundedFeeEstimator::new(&**fee_estimator);
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
                inner.queue_latest_holder_commitment_txn_for_broadcast(broadcaster, &fee_estimator, &logger);
        }
 
@@ -1592,7 +1635,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
        pub fn unsafe_get_latest_holder_commitment_txn<L: Deref>(&self, logger: &L) -> Vec<Transaction>
        where L::Target: Logger {
                let mut inner = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
                inner.unsafe_get_latest_holder_commitment_txn(&logger)
        }
 
@@ -1622,7 +1665,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                L::Target: Logger,
        {
                let mut inner = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
                inner.block_connected(
                        header, txdata, height, broadcaster, fee_estimator, &logger)
        }
@@ -1642,7 +1685,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                L::Target: Logger,
        {
                let mut inner = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
                inner.block_disconnected(
                        header, height, broadcaster, fee_estimator, &logger)
        }
@@ -1670,7 +1713,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
        {
                let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
                let mut inner = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
                inner.transactions_confirmed(
                        header, txdata, height, broadcaster, &bounded_fee_estimator, &logger)
        }
@@ -1694,7 +1737,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
        {
                let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
                let mut inner = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
                inner.transaction_unconfirmed(
                        txid, broadcaster, &bounded_fee_estimator, &logger
                );
@@ -1722,7 +1765,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
        {
                let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
                let mut inner = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
                inner.best_block_updated(
                        header, height, broadcaster, &bounded_fee_estimator, &logger
                )
@@ -1762,7 +1805,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
        {
                let fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
                let mut inner = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
                let current_height = inner.best_block.height;
                inner.onchain_tx_handler.rebroadcast_pending_claims(
                        current_height, FeerateStrategy::HighestOfPreviousOrNew, &broadcaster, &fee_estimator, &logger,
@@ -1781,7 +1824,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
        {
                let fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
                let mut inner = self.inner.lock().unwrap();
-               let logger = WithChannelMonitor::from_impl(logger, &*inner);
+               let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
                let current_height = inner.best_block.height;
                inner.onchain_tx_handler.rebroadcast_pending_claims(
                        current_height, FeerateStrategy::RetryPrevious, &broadcaster, &fee_estimator, &logger,
@@ -1821,6 +1864,55 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                spendable_outputs
        }
 
+       /// Checks if the monitor is fully resolved. Resolved monitor is one that has claimed all of
+       /// its outputs and balances (i.e. [`Self::get_claimable_balances`] returns an empty set).
+       ///
+       /// This function returns true only if [`Self::get_claimable_balances`] has been empty for at least
+       /// 4032 blocks as an additional protection against any bugs resulting in spuriously empty balance sets.
+       pub fn is_fully_resolved<L: Logger>(&self, logger: &L) -> bool {
+               let mut is_all_funds_claimed = self.get_claimable_balances().is_empty();
+               let current_height = self.current_best_block().height;
+               let mut inner = self.inner.lock().unwrap();
+
+               if is_all_funds_claimed {
+                       if !inner.funding_spend_seen {
+                               debug_assert!(false, "We should see funding spend by the time a monitor clears out");
+                               is_all_funds_claimed = false;
+                       }
+               }
+
+               const BLOCKS_THRESHOLD: u32 = 4032; // ~four weeks
+               match (inner.balances_empty_height, is_all_funds_claimed) {
+                       (Some(balances_empty_height), true) => {
+                               // Claimed all funds, check if reached the blocks threshold.
+                               return current_height >= balances_empty_height + BLOCKS_THRESHOLD;
+                       },
+                       (Some(_), false) => {
+                               // previously assumed we claimed all funds, but we have new funds to claim.
+                               // Should not happen in practice.
+                               debug_assert!(false, "Thought we were done claiming funds, but claimable_balances now has entries");
+                               log_error!(logger,
+                                       "WARNING: LDK thought it was done claiming all the available funds in the ChannelMonitor for channel {}, but later decided it had more to claim. This is potentially an important bug in LDK, please report it at https://github.com/lightningdevkit/rust-lightning/issues/new",
+                                       inner.get_funding_txo().0);
+                               inner.balances_empty_height = None;
+                               false
+                       },
+                       (None, true) => {
+                               // Claimed all funds but `balances_empty_height` is None. It is set to the
+                               // current block height.
+                               log_debug!(logger,
+                                       "ChannelMonitor funded at {} is now fully resolved. It will become archivable in {} blocks",
+                                       inner.get_funding_txo().0, BLOCKS_THRESHOLD);
+                               inner.balances_empty_height = Some(current_height);
+                               false
+                       },
+                       (None, false) => {
+                               // Have funds to claim.
+                               false
+                       },
+               }
+       }
+
        #[cfg(test)]
        pub fn get_counterparty_payment_script(&self) -> ScriptBuf {
                self.inner.lock().unwrap().counterparty_payment_script.clone()
@@ -1838,7 +1930,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
        }
 }
 
-impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
+impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
        /// Helper for get_claimable_balances which does the work for an individual HTLC, generating up
        /// to one `Balance` for the HTLC.
        fn get_htlc_balance(&self, htlc: &HTLCOutputInCommitment, holder_commitment: bool,
@@ -2017,7 +2109,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
        }
 }
 
-impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
+impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
        /// Gets the balances in this channel which are either claimable by us if we were to
        /// force-close the channel now or which are claimable on-chain (possibly awaiting
        /// confirmation).
@@ -2080,7 +2172,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                                                } else { None }
                                        }) {
                                                res.push(Balance::ClaimableAwaitingConfirmations {
-                                                       amount_satoshis: value,
+                                                       amount_satoshis: value.to_sat(),
                                                        confirmation_height: conf_thresh,
                                                });
                                        } else {
@@ -2103,7 +2195,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                                                        descriptor: SpendableOutputDescriptor::StaticOutput { output, .. }
                                                } = &event.event {
                                                        res.push(Balance::ClaimableAwaitingConfirmations {
-                                                               amount_satoshis: output.value,
+                                                               amount_satoshis: output.value.to_sat(),
                                                                confirmation_height: event.confirmation_threshold(),
                                                        });
                                                        if let Some(confirmed_to_self_idx) = confirmed_counterparty_output.map(|(idx, _)| idx) {
@@ -2122,7 +2214,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                                                        .is_output_spend_pending(&BitcoinOutPoint::new(txid, confirmed_to_self_idx));
                                                if output_spendable {
                                                        res.push(Balance::CounterpartyRevokedOutputClaimable {
-                                                               amount_satoshis: amt,
+                                                               amount_satoshis: amt.to_sat(),
                                                        });
                                                }
                                        } else {
@@ -2429,7 +2521,7 @@ pub fn deliberately_bogus_accepted_htlc_witness() -> Vec<Vec<u8>> {
        vec![Vec::new(), Vec::new(), Vec::new(), Vec::new(), deliberately_bogus_accepted_htlc_witness_program().into()].into()
 }
 
-impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
+impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
        /// Inserts a revocation secret into this channel monitor. Prunes old preimages if neither
        /// needed by holder commitment transactions HTCLs nor by counterparty ones. Unless we haven't already seen
        /// counterparty commitment transaction's secret, they are de facto pruned (we can use revocation key).
@@ -2727,7 +2819,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                }
        }
 
-       fn generate_claimable_outpoints_and_watch_outputs(&mut self) -> (Vec<PackageTemplate>, Vec<TransactionOutputs>) {
+       fn generate_claimable_outpoints_and_watch_outputs(&mut self, reason: ClosureReason) -> (Vec<PackageTemplate>, Vec<TransactionOutputs>) {
                let funding_outp = HolderFundingOutput::build(
                        self.funding_redeemscript.clone(),
                        self.channel_value_satoshis,
@@ -2739,7 +2831,13 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                        self.best_block.height, self.best_block.height
                );
                let mut claimable_outpoints = vec![commitment_package];
-               self.pending_monitor_events.push(MonitorEvent::HolderForceClosed(self.funding_info.0));
+               let event = MonitorEvent::HolderForceClosedWithInfo {
+                       reason,
+                       outpoint: self.funding_info.0,
+                       channel_id: self.channel_id,
+               };
+               self.pending_monitor_events.push(event);
+
                // Although we aren't signing the transaction directly here, the transaction will be signed
                // in the claim that is queued to OnchainTxHandler. We set holder_tx_signed here to reject
                // new channel updates.
@@ -2775,7 +2873,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                F::Target: FeeEstimator,
                L::Target: Logger,
        {
-               let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs();
+               let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs(ClosureReason::HolderForceClosed);
                self.onchain_tx_handler.update_claims_view_from_requests(
                        claimable_outpoints, self.best_block.height, self.best_block.height, broadcaster,
                        fee_estimator, logger
@@ -2967,7 +3065,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                                        debug_assert_eq!(self.current_holder_commitment_tx.txid, commitment_txid);
                                        let pending_htlcs = self.current_holder_commitment_tx.non_dust_htlcs();
                                        let commitment_tx_fee_satoshis = self.channel_value_satoshis -
-                                               commitment_tx.output.iter().fold(0u64, |sum, output| sum + output.value);
+                                               commitment_tx.output.iter().fold(0u64, |sum, output| sum + output.value.to_sat());
                                        ret.push(Event::BumpTransaction(BumpTransactionEvent::ChannelClose {
                                                channel_id,
                                                counterparty_node_id,
@@ -3107,7 +3205,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
 
                let sig = self.onchain_tx_handler.signer.sign_justice_revoked_output(
                        &justice_tx, input_idx, value, &per_commitment_key, &self.onchain_tx_handler.secp_ctx)?;
-               justice_tx.input[input_idx].witness.push_bitcoin_signature(&sig.serialize_der(), EcdsaSighashType::All);
+               justice_tx.input[input_idx].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(sig));
                justice_tx.input[input_idx].witness.push(&[1u8]);
                justice_tx.input[input_idx].witness.push(revokeable_redeemscript.as_bytes());
                Ok(justice_tx)
@@ -3168,7 +3266,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                        let delayed_key = DelayedPaymentKey::from_basepoint(&self.onchain_tx_handler.secp_ctx, &self.counterparty_commitment_params.counterparty_delayed_payment_base_key, &PublicKey::from_secret_key(&self.onchain_tx_handler.secp_ctx, &per_commitment_key));
 
                        let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key);
-                       let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
+                       let revokeable_p2wsh = revokeable_redeemscript.to_p2wsh();
 
                        // First, process non-htlc outputs (to_holder & to_counterparty)
                        for (idx, outp) in tx.output.iter().enumerate() {
@@ -3186,7 +3284,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                                for (_, &(ref htlc, _)) in per_commitment_data.iter().enumerate() {
                                        if let Some(transaction_output_index) = htlc.transaction_output_index {
                                                if transaction_output_index as usize >= tx.output.len() ||
-                                                               tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
+                                                               tx.output[transaction_output_index as usize].value != htlc.to_bitcoin_amount() {
                                                        // per_commitment_data is corrupt or our commitment signing key leaked!
                                                        return (claimable_outpoints, (commitment_txid, watch_outputs),
                                                                to_counterparty_output_info);
@@ -3288,7 +3386,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
 
                        let revokeable_p2wsh = chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
                                self.counterparty_commitment_params.on_counterparty_tx_csv,
-                               &delayed_key).to_v0_p2wsh();
+                               &delayed_key).to_p2wsh();
                        for (idx, outp) in transaction.output.iter().enumerate() {
                                if outp.script_pubkey == revokeable_p2wsh {
                                        to_counterparty_output_info =
@@ -3301,7 +3399,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                        if let Some(transaction_output_index) = htlc.transaction_output_index {
                                if let Some(transaction) = tx {
                                        if transaction_output_index as usize >= transaction.output.len() ||
-                                               transaction.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
+                                               transaction.output[transaction_output_index as usize].value != htlc.to_bitcoin_amount() {
                                                        // per_commitment_data is corrupt or our commitment signing key leaked!
                                                        return (claimable_outpoints, to_counterparty_output_info);
                                                }
@@ -3384,7 +3482,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                let mut claim_requests = Vec::with_capacity(holder_tx.htlc_outputs.len());
 
                let redeemscript = chan_utils::get_revokeable_redeemscript(&holder_tx.revocation_key, self.on_holder_tx_csv, &holder_tx.delayed_payment_key);
-               let broadcasted_holder_revokable_script = Some((redeemscript.to_v0_p2wsh(), holder_tx.per_commitment_point.clone(), holder_tx.revocation_key.clone()));
+               let broadcasted_holder_revokable_script = Some((redeemscript.to_p2wsh(), holder_tx.per_commitment_point.clone(), holder_tx.revocation_key.clone()));
 
                for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
                        if let Some(transaction_output_index) = htlc.transaction_output_index {
@@ -3622,11 +3720,11 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
        {
                let txn_matched = self.filter_block(txdata);
                for tx in &txn_matched {
-                       let mut output_val = 0;
+                       let mut output_val = Amount::ZERO;
                        for out in tx.output.iter() {
-                               if out.value > 21_000_000_0000_0000 { panic!("Value-overflowing transaction provided to block connected"); }
+                               if out.value > Amount::MAX_MONEY { panic!("Value-overflowing transaction provided to block connected"); }
                                output_val += out.value;
-                               if output_val > 21_000_000_0000_0000 { panic!("Value-overflowing transaction provided to block connected"); }
+                               if output_val > Amount::MAX_MONEY { panic!("Value-overflowing transaction provided to block connected"); }
                        }
                }
 
@@ -3778,7 +3876,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
 
                let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
                if should_broadcast {
-                       let (mut new_outpoints, mut new_outputs) = self.generate_claimable_outpoints_and_watch_outputs();
+                       let (mut new_outpoints, mut new_outputs) = self.generate_claimable_outpoints_and_watch_outputs(ClosureReason::HTLCsTimedOut);
                        claimable_outpoints.append(&mut new_outpoints);
                        watch_outputs.append(&mut new_outputs);
                }
@@ -3972,7 +4070,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                                                        // If the expected script is a known type, check that the witness
                                                        // appears to be spending the correct type (ie that the match would
                                                        // actually succeed in BIP 158/159-style filters).
-                                                       if _script_pubkey.is_v0_p2wsh() {
+                                                       if _script_pubkey.is_p2wsh() {
                                                                if input.witness.last().unwrap().to_vec() == deliberately_bogus_accepted_htlc_witness_program() {
                                                                        // In at least one test we use a deliberately bogus witness
                                                                        // script which hit an old panic. Thus, we check for that here
@@ -3981,7 +4079,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                                                                }
 
                                                                assert_eq!(&bitcoin::Address::p2wsh(&ScriptBuf::from(input.witness.last().unwrap().to_vec()), bitcoin::Network::Bitcoin).script_pubkey(), _script_pubkey);
-                                                       } else if _script_pubkey.is_v0_p2wpkh() {
+                                                       } else if _script_pubkey.is_p2wpkh() {
                                                                assert_eq!(&bitcoin::Address::p2wpkh(&bitcoin::PublicKey::from_slice(&input.witness.last().unwrap()).unwrap(), bitcoin::Network::Bitcoin).unwrap().script_pubkey(), _script_pubkey);
                                                        } else { panic!(); }
                                                }
@@ -4289,6 +4387,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                                                revocation_pubkey: broadcasted_holder_revokable_script.2,
                                                channel_keys_id: self.channel_keys_id,
                                                channel_value_satoshis: self.channel_value_satoshis,
+                                               channel_transaction_parameters: Some(self.onchain_tx_handler.channel_transaction_parameters.clone()),
                                        }));
                                }
                        }
@@ -4331,7 +4430,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
        }
 }
 
-impl<Signer: WriteableEcdsaChannelSigner, T: Deref, F: Deref, L: Deref> chain::Listen for (ChannelMonitor<Signer>, T, F, L)
+impl<Signer: EcdsaChannelSigner, T: Deref, F: Deref, L: Deref> chain::Listen for (ChannelMonitor<Signer>, T, F, L)
 where
        T::Target: BroadcasterInterface,
        F::Target: FeeEstimator,
@@ -4346,7 +4445,7 @@ where
        }
 }
 
-impl<Signer: WriteableEcdsaChannelSigner, M, T: Deref, F: Deref, L: Deref> chain::Confirm for (M, T, F, L)
+impl<Signer: EcdsaChannelSigner, M, T: Deref, F: Deref, L: Deref> chain::Confirm for (M, T, F, L)
 where
        M: Deref<Target = ChannelMonitor<Signer>>,
        T::Target: BroadcasterInterface,
@@ -4591,6 +4690,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
                let mut spendable_txids_confirmed = Some(Vec::new());
                let mut counterparty_fulfilled_htlcs = Some(new_hash_map());
                let mut initial_counterparty_commitment_info = None;
+               let mut balances_empty_height = None;
                let mut channel_id = None;
                read_tlv_fields!(reader, {
                        (1, funding_spend_confirmed, option),
@@ -4603,17 +4703,28 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
                        (15, counterparty_fulfilled_htlcs, option),
                        (17, initial_counterparty_commitment_info, option),
                        (19, channel_id, option),
+                       (21, balances_empty_height, option),
                });
 
+               // `HolderForceClosedWithInfo` replaced `HolderForceClosed` in v0.0.122. If we have both
+               // events, we can remove the `HolderForceClosed` event and just keep the `HolderForceClosedWithInfo`.
+               if let Some(ref mut pending_monitor_events) = pending_monitor_events {
+                       if pending_monitor_events.iter().any(|e| matches!(e, MonitorEvent::HolderForceClosed(_))) &&
+                               pending_monitor_events.iter().any(|e| matches!(e, MonitorEvent::HolderForceClosedWithInfo { .. }))
+                       {
+                               pending_monitor_events.retain(|e| !matches!(e, MonitorEvent::HolderForceClosed(_)));
+                       }
+               }
+
                // Monitors for anchor outputs channels opened in v0.0.116 suffered from a bug in which the
                // wrong `counterparty_payment_script` was being tracked. Fix it now on deserialization to
                // give them a chance to recognize the spendable output.
                if onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx() &&
-                       counterparty_payment_script.is_v0_p2wpkh()
+                       counterparty_payment_script.is_p2wpkh()
                {
                        let payment_point = onchain_tx_handler.channel_transaction_parameters.holder_pubkeys.payment_point;
                        counterparty_payment_script =
-                               chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point).to_v0_p2wsh();
+                               chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point).to_p2wsh();
                }
 
                Ok((best_block.block_hash, ChannelMonitor::from_impl(ChannelMonitorImpl {
@@ -4671,16 +4782,18 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
                        best_block,
                        counterparty_node_id,
                        initial_counterparty_commitment_info,
+                       balances_empty_height,
                })))
        }
 }
 
 #[cfg(test)]
 mod tests {
+       use bitcoin::amount::Amount;
        use bitcoin::blockdata::locktime::absolute::LockTime;
        use bitcoin::blockdata::script::{ScriptBuf, Builder};
        use bitcoin::blockdata::opcodes;
-       use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut};
+       use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut, Version};
        use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
        use bitcoin::sighash;
        use bitcoin::sighash::EcdsaSighashType;
@@ -4688,7 +4801,7 @@ mod tests {
        use bitcoin::hashes::sha256::Hash as Sha256;
        use bitcoin::hashes::hex::FromHex;
        use bitcoin::hash_types::{BlockHash, Txid};
-       use bitcoin::network::constants::Network;
+       use bitcoin::network::Network;
        use bitcoin::secp256k1::{SecretKey,PublicKey};
        use bitcoin::secp256k1::Secp256k1;
        use bitcoin::{Sequence, Witness};
@@ -4702,7 +4815,7 @@ mod tests {
        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::sign::InMemorySigner;
-       use crate::ln::{PaymentPreimage, PaymentHash, ChannelId};
+       use crate::ln::types::{PaymentPreimage, PaymentHash, ChannelId};
        use crate::ln::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, RevocationBasepoint, RevocationKey};
        use crate::ln::chan_utils::{self,HTLCOutputInCommitment, ChannelPublicKeys, ChannelTransactionParameters, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters};
        use crate::ln::channelmanager::{PaymentSendFailure, PaymentId, RecipientOnionFields};
@@ -4715,6 +4828,8 @@ mod tests {
        use crate::sync::{Arc, Mutex};
        use crate::io;
        use crate::ln::features::ChannelTypeFeatures;
+
+       #[allow(unused_imports)]
        use crate::prelude::*;
 
        use std::str::FromStr;
@@ -4846,7 +4961,7 @@ mod tests {
                        }
                }
                let dummy_sig = crate::crypto::utils::sign(&secp_ctx,
-                       &bitcoin::secp256k1::Message::from_slice(&[42; 32]).unwrap(),
+                       &bitcoin::secp256k1::Message::from_digest([42; 32]),
                        &SecretKey::from_slice(&[42; 32]).unwrap());
 
                macro_rules! test_preimages_exist {
@@ -4978,7 +5093,7 @@ mod tests {
                                        transaction_output_index: Some($idx as u32),
                                };
                                let redeem_script = if *$weight == WEIGHT_REVOKED_OUTPUT { chan_utils::get_revokeable_redeemscript(&RevocationKey::from_basepoint(&secp_ctx, &RevocationBasepoint::from(pubkey), &pubkey), 256, &DelayedPaymentKey::from_basepoint(&secp_ctx, &DelayedPaymentBasepoint::from(pubkey), &pubkey)) } else { chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, $opt_anchors, &HtlcKey::from_basepoint(&secp_ctx, &HtlcBasepoint::from(pubkey), &pubkey), &HtlcKey::from_basepoint(&secp_ctx, &HtlcBasepoint::from(pubkey), &pubkey), &RevocationKey::from_basepoint(&secp_ctx, &RevocationBasepoint::from(pubkey), &pubkey)) };
-                               let sighash = hash_to_message!(&$sighash_parts.segwit_signature_hash($idx, &redeem_script, $amount, EcdsaSighashType::All).unwrap()[..]);
+                               let sighash = hash_to_message!(&$sighash_parts.p2wsh_signature_hash($idx, &redeem_script, $amount, EcdsaSighashType::All).unwrap()[..]);
                                let sig = secp_ctx.sign_ecdsa(&sighash, &privkey);
                                let mut ser_sig = sig.serialize_der().to_vec();
                                ser_sig.push(EcdsaSighashType::All as u8);
@@ -5007,7 +5122,7 @@ mod tests {
 
                // Justice tx with 1 to_holder, 2 revoked offered HTLCs, 1 revoked received HTLCs
                for channel_type_features in [ChannelTypeFeatures::only_static_remote_key(), ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()].iter() {
-                       let mut claim_tx = Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
+                       let mut claim_tx = Transaction { version: Version(0), lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
                        let mut sum_actual_sigs = 0;
                        for i in 0..4 {
                                claim_tx.input.push(TxIn {
@@ -5022,7 +5137,7 @@ mod tests {
                        }
                        claim_tx.output.push(TxOut {
                                script_pubkey: script_pubkey.clone(),
-                               value: 0,
+                               value: Amount::ZERO,
                        });
                        let base_weight = claim_tx.weight().to_wu();
                        let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT, weight_revoked_offered_htlc(channel_type_features), weight_revoked_offered_htlc(channel_type_features), weight_revoked_received_htlc(channel_type_features)];
@@ -5030,7 +5145,7 @@ mod tests {
                        {
                                let mut sighash_parts = sighash::SighashCache::new(&mut claim_tx);
                                for (idx, inp) in inputs_weight.iter().enumerate() {
-                                       sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, channel_type_features);
+                                       sign_input!(sighash_parts, idx, Amount::ZERO, inp, sum_actual_sigs, channel_type_features);
                                        inputs_total_weight += inp;
                                }
                        }
@@ -5039,7 +5154,7 @@ mod tests {
 
                // Claim tx with 1 offered HTLCs, 3 received HTLCs
                for channel_type_features in [ChannelTypeFeatures::only_static_remote_key(), ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()].iter() {
-                       let mut claim_tx = Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
+                       let mut claim_tx = Transaction { version: Version(0), lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
                        let mut sum_actual_sigs = 0;
                        for i in 0..4 {
                                claim_tx.input.push(TxIn {
@@ -5054,7 +5169,7 @@ mod tests {
                        }
                        claim_tx.output.push(TxOut {
                                script_pubkey: script_pubkey.clone(),
-                               value: 0,
+                               value: Amount::ZERO,
                        });
                        let base_weight = claim_tx.weight().to_wu();
                        let inputs_weight = vec![weight_offered_htlc(channel_type_features), weight_received_htlc(channel_type_features), weight_received_htlc(channel_type_features), weight_received_htlc(channel_type_features)];
@@ -5062,7 +5177,7 @@ mod tests {
                        {
                                let mut sighash_parts = sighash::SighashCache::new(&mut claim_tx);
                                for (idx, inp) in inputs_weight.iter().enumerate() {
-                                       sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, channel_type_features);
+                                       sign_input!(sighash_parts, idx, Amount::ZERO, inp, sum_actual_sigs, channel_type_features);
                                        inputs_total_weight += inp;
                                }
                        }
@@ -5071,7 +5186,7 @@ mod tests {
 
                // Justice tx with 1 revoked HTLC-Success tx output
                for channel_type_features in [ChannelTypeFeatures::only_static_remote_key(), ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()].iter() {
-                       let mut claim_tx = Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
+                       let mut claim_tx = Transaction { version: Version(0), lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
                        let mut sum_actual_sigs = 0;
                        claim_tx.input.push(TxIn {
                                previous_output: BitcoinOutPoint {
@@ -5084,7 +5199,7 @@ mod tests {
                        });
                        claim_tx.output.push(TxOut {
                                script_pubkey: script_pubkey.clone(),
-                               value: 0,
+                               value: Amount::ZERO,
                        });
                        let base_weight = claim_tx.weight().to_wu();
                        let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT];
@@ -5092,7 +5207,7 @@ mod tests {
                        {
                                let mut sighash_parts = sighash::SighashCache::new(&mut claim_tx);
                                for (idx, inp) in inputs_weight.iter().enumerate() {
-                                       sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, channel_type_features);
+                                       sign_input!(sighash_parts, idx, Amount::ZERO, inp, sum_actual_sigs, channel_type_features);
                                        inputs_total_weight += inp;
                                }
                        }
@@ -5149,7 +5264,8 @@ mod tests {
                        best_block, dummy_key, channel_id);
 
                let chan_id = monitor.inner.lock().unwrap().channel_id();
-               let context_logger = WithChannelMonitor::from(&logger, &monitor);
+               let payment_hash = PaymentHash([1; 32]);
+               let context_logger = WithChannelMonitor::from(&logger, &monitor, Some(payment_hash));
                log_error!(context_logger, "This is an error");
                log_warn!(context_logger, "This is an error");
                log_debug!(context_logger, "This is an error");
index 356520b5cba86e647acdec77fd0cb86b683e7d58..e43c08d55e513a906000d52a2fd6795580bb5037 100644 (file)
@@ -13,14 +13,16 @@ use bitcoin::blockdata::block::{Block, Header};
 use bitcoin::blockdata::constants::genesis_block;
 use bitcoin::blockdata::script::{Script, ScriptBuf};
 use bitcoin::hash_types::{BlockHash, Txid};
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 use bitcoin::secp256k1::PublicKey;
 
 use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, MonitorEvent};
-use crate::ln::ChannelId;
-use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
+use crate::ln::types::ChannelId;
+use crate::sign::ecdsa::EcdsaChannelSigner;
 use crate::chain::transaction::{OutPoint, TransactionData};
+use crate::impl_writeable_tlv_based;
 
+#[allow(unused_imports)]
 use crate::prelude::*;
 
 pub mod chaininterface;
@@ -50,11 +52,19 @@ impl BestBlock {
        }
 
        /// Returns a `BestBlock` as identified by the given block hash and height.
+       ///
+       /// This is not exported to bindings users directly as the bindings auto-generate an
+       /// equivalent `new`.
        pub fn new(block_hash: BlockHash, height: u32) -> Self {
                BestBlock { block_hash, height }
        }
 }
 
+impl_writeable_tlv_based!(BestBlock, {
+       (0, block_hash, required),
+       (2, height, required),
+});
+
 
 /// The `Listen` trait is used to notify when blocks have been connected or disconnected from the
 /// chain.
@@ -250,7 +260,7 @@ pub enum ChannelMonitorUpdateStatus {
 /// application crashes.
 ///
 /// See method documentation and [`ChannelMonitorUpdateStatus`] for specific requirements.
-pub trait Watch<ChannelSigner: WriteableEcdsaChannelSigner> {
+pub trait Watch<ChannelSigner: EcdsaChannelSigner> {
        /// Watches a channel identified by `funding_txo` using `monitor`.
        ///
        /// Implementations are responsible for watching the chain for the funding transaction along
@@ -320,6 +330,11 @@ pub trait Watch<ChannelSigner: WriteableEcdsaChannelSigner> {
 pub trait Filter {
        /// Registers interest in a transaction with `txid` and having an output with `script_pubkey` as
        /// a spending condition.
+       ///
+       /// This may be used, for example, to monitor for when a funding transaction confirms.
+       ///
+       /// The `script_pubkey` is provided for informational purposes and may be useful for block
+       /// sources which only support filtering on scripts.
        fn register_tx(&self, txid: &Txid, script_pubkey: &Script);
 
        /// Registers interest in spends of a transaction output.
@@ -328,6 +343,9 @@ pub trait Filter {
        /// to ensure that also dependent output spents within an already connected block are correctly
        /// handled, e.g., by re-scanning the block in question whenever new outputs have been
        /// registered mid-processing.
+       ///
+       /// This may be used, for example, to monitor for when a funding output is spent (by any
+       /// transaction).
        fn register_output(&self, output: WatchedOutput);
 }
 
@@ -340,6 +358,9 @@ pub trait Filter {
 /// If `block_hash` is `Some`, this indicates the output was created in the corresponding block and
 /// may have been spent there. See [`Filter::register_output`] for details.
 ///
+/// Depending on your block source, you may need one or both of either [`Self::outpoint`] or
+/// [`Self::script_pubkey`].
+///
 /// [`ChannelMonitor`]: channelmonitor::ChannelMonitor
 /// [`ChannelMonitor::block_connected`]: channelmonitor::ChannelMonitor::block_connected
 #[derive(Clone, PartialEq, Eq, Hash)]
index 94d6aa35746caa3a6bbc10e991a7161d54aaef2c..be097b637d0a4eb69974b712bb090931feaec81d 100644 (file)
@@ -12,6 +12,7 @@
 //! OnchainTxHandler objects are fully-part of ChannelMonitor and encapsulates all
 //! building, tracking, bumping and notifications functions.
 
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::locktime::absolute::LockTime;
 use bitcoin::blockdata::transaction::Transaction;
 use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
@@ -23,9 +24,9 @@ use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
 use bitcoin::secp256k1;
 
 use crate::chain::chaininterface::compute_feerate_sat_per_1000_weight;
-use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, ChannelSigner, EntropySource, SignerProvider, ecdsa::WriteableEcdsaChannelSigner};
+use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, ChannelSigner, EntropySource, SignerProvider, ecdsa::EcdsaChannelSigner};
 use crate::ln::msgs::DecodeError;
-use crate::ln::PaymentPreimage;
+use crate::ln::types::PaymentPreimage;
 use crate::ln::chan_utils::{self, ChannelTransactionParameters, HTLCOutputInCommitment, HolderCommitmentTransaction};
 use crate::chain::ClaimId;
 use crate::chain::chaininterface::{ConfirmationTarget, FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator};
@@ -33,7 +34,7 @@ use crate::chain::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER};
 use crate::chain::package::{PackageSolvingData, PackageTemplate};
 use crate::chain::transaction::MaybeSignedTransaction;
 use crate::util::logger::Logger;
-use crate::util::ser::{Readable, ReadableArgs, MaybeReadable, UpgradableRequired, Writer, Writeable, VecWriter};
+use crate::util::ser::{Readable, ReadableArgs, MaybeReadable, UpgradableRequired, Writer, Writeable};
 
 use crate::io;
 use crate::prelude::*;
@@ -225,7 +226,7 @@ pub(crate) enum FeerateStrategy {
 /// OnchainTxHandler receives claiming requests, aggregates them if it's sound, broadcast and
 /// do RBF bumping if possible.
 #[derive(Clone)]
-pub struct OnchainTxHandler<ChannelSigner: WriteableEcdsaChannelSigner> {
+pub struct OnchainTxHandler<ChannelSigner: EcdsaChannelSigner> {
        channel_value_satoshis: u64,
        channel_keys_id: [u8; 32],
        destination_script: ScriptBuf,
@@ -281,7 +282,7 @@ pub struct OnchainTxHandler<ChannelSigner: WriteableEcdsaChannelSigner> {
        pub(super) secp_ctx: Secp256k1<secp256k1::All>,
 }
 
-impl<ChannelSigner: WriteableEcdsaChannelSigner> PartialEq for OnchainTxHandler<ChannelSigner> {
+impl<ChannelSigner: EcdsaChannelSigner> PartialEq for OnchainTxHandler<ChannelSigner> {
        fn eq(&self, other: &Self) -> bool {
                // `signer`, `secp_ctx`, and `pending_claim_events` are excluded on purpose.
                self.channel_value_satoshis == other.channel_value_satoshis &&
@@ -300,7 +301,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> PartialEq for OnchainTxHandler<
 const SERIALIZATION_VERSION: u8 = 1;
 const MIN_SERIALIZATION_VERSION: u8 = 1;
 
-impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
+impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
        pub(crate) fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
                write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
 
@@ -312,12 +313,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
 
                self.channel_transaction_parameters.write(writer)?;
 
-               let mut key_data = VecWriter(Vec::new());
-               self.signer.write(&mut key_data)?;
-               assert!(key_data.0.len() < core::usize::MAX);
-               assert!(key_data.0.len() < core::u32::MAX as usize);
-               (key_data.0.len() as u32).write(writer)?;
-               writer.write_all(&key_data.0[..])?;
+               // Write a zero-length signer. The data is no longer deserialized as of version 0.0.113 and
+               // downgrades before version 0.0.113 are no longer supported as of version 0.0.119.
+               0u32.write(writer)?;
 
                writer.write_all(&(self.pending_claim_requests.len() as u64).to_be_bytes())?;
                for (ref ancestor_claim_txid, request) in self.pending_claim_requests.iter() {
@@ -443,7 +441,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
        }
 }
 
-impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
+impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
        pub(crate) fn new(
                channel_value_satoshis: u64, channel_keys_id: [u8; 32], destination_script: ScriptBuf,
                signer: ChannelSigner, channel_parameters: ChannelTransactionParameters,
@@ -618,7 +616,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
                                assert!(new_feerate != 0);
 
                                let transaction = cached_request.maybe_finalize_malleable_package(
-                                       cur_height, self, output_value, self.destination_script.clone(), logger
+                                       cur_height, self, Amount::from_sat(output_value), self.destination_script.clone(), logger
                                ).unwrap();
                                assert!(predicted_weight >= transaction.0.weight().to_wu());
                                return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)));
@@ -647,7 +645,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
                                        let package_target_feerate_sat_per_1000_weight = cached_request
                                                .compute_package_feerate(fee_estimator, conf_target, feerate_strategy);
                                        if let Some(input_amount_sat) = output.funding_amount {
-                                               let fee_sat = input_amount_sat - tx.0.output.iter().map(|output| output.value).sum::<u64>();
+                                               let fee_sat = input_amount_sat - tx.0.output.iter().map(|output| output.value.to_sat()).sum::<u64>();
                                                let commitment_tx_feerate_sat_per_1000_weight =
                                                        compute_feerate_sat_per_1000_weight(fee_sat, tx.0.weight().to_wu());
                                                if commitment_tx_feerate_sat_per_1000_weight >= package_target_feerate_sat_per_1000_weight {
index e304b16ef3e4fb1d42630cf9b568cc2014034200..906e730dbf248325e4d5e8ab45d00fba29b4a2db 100644 (file)
@@ -13,6 +13,7 @@
 
 
 use bitcoin::{Sequence, Witness};
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
 use bitcoin::blockdata::locktime::absolute::LockTime;
 use bitcoin::blockdata::transaction::{TxOut,TxIn, Transaction};
@@ -21,26 +22,28 @@ use bitcoin::blockdata::script::{Script, ScriptBuf};
 use bitcoin::hash_types::Txid;
 use bitcoin::secp256k1::{SecretKey,PublicKey};
 use bitcoin::sighash::EcdsaSighashType;
+use bitcoin::transaction::Version;
 
-use crate::ln::PaymentPreimage;
+use crate::ln::types::PaymentPreimage;
 use crate::ln::chan_utils::{self, TxCreationKeys, HTLCOutputInCommitment};
 use crate::ln::features::ChannelTypeFeatures;
 use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint};
 use crate::ln::msgs::DecodeError;
 use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT, compute_feerate_sat_per_1000_weight, FEERATE_FLOOR_SATS_PER_KW};
 use crate::chain::transaction::MaybeSignedTransaction;
-use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
+use crate::sign::ecdsa::EcdsaChannelSigner;
 use crate::chain::onchaintx::{FeerateStrategy, ExternalHTLCClaim, OnchainTxHandler};
 use crate::util::logger::Logger;
 use crate::util::ser::{Readable, Writer, Writeable, RequiredWrapper};
 
 use crate::io;
-use crate::prelude::*;
 use core::cmp;
-use core::convert::TryInto;
 use core::mem;
 use core::ops::Deref;
 
+#[allow(unused_imports)]
+use crate::prelude::*;
+
 use super::chaininterface::LowerBoundedFeeEstimator;
 
 const MAX_ALLOC_SIZE: usize = 64*1024;
@@ -120,13 +123,13 @@ pub(crate) struct RevokedOutput {
        counterparty_htlc_base_key: HtlcBasepoint,
        per_commitment_key: SecretKey,
        weight: u64,
-       amount: u64,
+       amount: Amount,
        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: DelayedPaymentBasepoint, counterparty_htlc_base_key: HtlcBasepoint, per_commitment_key: SecretKey, amount: u64, on_counterparty_tx_csv: u16, is_counterparty_balance_on_anchors: bool) -> Self {
+       pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, counterparty_htlc_base_key: HtlcBasepoint, per_commitment_key: SecretKey, amount: Amount, on_counterparty_tx_csv: u16, is_counterparty_balance_on_anchors: bool) -> Self {
                RevokedOutput {
                        per_commitment_point,
                        counterparty_delayed_payment_base_key,
@@ -500,7 +503,7 @@ pub(crate) enum PackageSolvingData {
 impl PackageSolvingData {
        fn amount(&self) -> u64 {
                let amt = match self {
-                       PackageSolvingData::RevokedOutput(ref outp) => outp.amount,
+                       PackageSolvingData::RevokedOutput(ref outp) => outp.amount.to_sat(),
                        PackageSolvingData::RevokedHTLCOutput(ref outp) => outp.amount,
                        PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => outp.htlc.amount_msat / 1000,
                        PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => outp.htlc.amount_msat / 1000,
@@ -579,13 +582,13 @@ impl PackageSolvingData {
                        witness: Witness::new(),
                }
        }
-       fn finalize_input<Signer: WriteableEcdsaChannelSigner>(&self, bumped_tx: &mut Transaction, i: usize, onchain_handler: &mut OnchainTxHandler<Signer>) -> bool {
+       fn finalize_input<Signer: EcdsaChannelSigner>(&self, bumped_tx: &mut Transaction, i: usize, onchain_handler: &mut OnchainTxHandler<Signer>) -> bool {
                match self {
                        PackageSolvingData::RevokedOutput(ref outp) => {
                                let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint);
                                let witness_script = chan_utils::get_revokeable_redeemscript(&chan_keys.revocation_key, outp.on_counterparty_tx_csv, &chan_keys.broadcaster_delayed_payment_key);
                                //TODO: should we panic on signer failure ?
-                               if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_output(&bumped_tx, i, outp.amount, &outp.per_commitment_key, &onchain_handler.secp_ctx) {
+                               if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_output(&bumped_tx, i, outp.amount.to_sat(), &outp.per_commitment_key, &onchain_handler.secp_ctx) {
                                        let mut ser_sig = sig.serialize_der().to_vec();
                                        ser_sig.push(EcdsaSighashType::All as u8);
                                        bumped_tx.input[i].witness.push(ser_sig);
@@ -634,7 +637,7 @@ impl PackageSolvingData {
                }
                true
        }
-       fn get_maybe_finalized_tx<Signer: WriteableEcdsaChannelSigner>(&self, outpoint: &BitcoinOutPoint, onchain_handler: &mut OnchainTxHandler<Signer>) -> Option<MaybeSignedTransaction> {
+       fn get_maybe_finalized_tx<Signer: EcdsaChannelSigner>(&self, outpoint: &BitcoinOutPoint, onchain_handler: &mut OnchainTxHandler<Signer>) -> Option<MaybeSignedTransaction> {
                match self {
                        PackageSolvingData::HolderHTLCOutput(ref outp) => {
                                debug_assert!(!outp.channel_type_features.supports_anchors_zero_fee_htlc_tx());
@@ -891,7 +894,7 @@ impl PackageTemplate {
                let output_weight = (8 + 1 + destination_script.len()) * WITNESS_SCALE_FACTOR;
                (inputs_weight + witnesses_weight + transaction_weight + output_weight) as u64
        }
-       pub(crate) fn construct_malleable_package_with_external_funding<Signer: WriteableEcdsaChannelSigner>(
+       pub(crate) fn construct_malleable_package_with_external_funding<Signer: EcdsaChannelSigner>(
                &self, onchain_handler: &mut OnchainTxHandler<Signer>,
        ) -> Option<Vec<ExternalHTLCClaim>> {
                debug_assert!(self.requires_external_funding());
@@ -909,13 +912,13 @@ impl PackageTemplate {
                }
                htlcs
        }
-       pub(crate) fn maybe_finalize_malleable_package<L: Logger, Signer: WriteableEcdsaChannelSigner>(
-               &self, current_height: u32, onchain_handler: &mut OnchainTxHandler<Signer>, value: u64,
+       pub(crate) fn maybe_finalize_malleable_package<L: Logger, Signer: EcdsaChannelSigner>(
+               &self, current_height: u32, onchain_handler: &mut OnchainTxHandler<Signer>, value: Amount,
                destination_script: ScriptBuf, logger: &L
        ) -> Option<MaybeSignedTransaction> {
                debug_assert!(self.is_malleable());
                let mut bumped_tx = Transaction {
-                       version: 2,
+                       version: Version::TWO,
                        lock_time: LockTime::from_consensus(self.package_locktime(current_height)),
                        input: vec![],
                        output: vec![TxOut {
@@ -932,7 +935,7 @@ impl PackageTemplate {
                }
                Some(MaybeSignedTransaction(bumped_tx))
        }
-       pub(crate) fn maybe_finalize_untractable_package<L: Logger, Signer: WriteableEcdsaChannelSigner>(
+       pub(crate) fn maybe_finalize_untractable_package<L: Logger, Signer: EcdsaChannelSigner>(
                &self, onchain_handler: &mut OnchainTxHandler<Signer>, logger: &L,
        ) -> Option<MaybeSignedTransaction> {
                debug_assert!(!self.is_malleable());
@@ -1194,9 +1197,10 @@ mod tests {
        use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderHTLCOutput, PackageTemplate, PackageSolvingData, RevokedOutput, WEIGHT_REVOKED_OUTPUT, weight_offered_htlc, weight_received_htlc};
        use crate::chain::Txid;
        use crate::ln::chan_utils::HTLCOutputInCommitment;
-       use crate::ln::{PaymentPreimage, PaymentHash};
+       use crate::ln::types::{PaymentPreimage, PaymentHash};
        use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint};
 
+       use bitcoin::amount::Amount;
        use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
        use bitcoin::blockdata::script::ScriptBuf;
        use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
@@ -1214,7 +1218,7 @@ mod tests {
                        {
                                let dumb_scalar = SecretKey::from_slice(&<Vec<u8>>::from_hex("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap();
                                let dumb_point = PublicKey::from_secret_key(&$secp_ctx, &dumb_scalar);
-                               PackageSolvingData::RevokedOutput(RevokedOutput::build(dumb_point, DelayedPaymentBasepoint::from(dumb_point), HtlcBasepoint::from(dumb_point), dumb_scalar, 0, 0, $is_counterparty_balance_on_anchors))
+                               PackageSolvingData::RevokedOutput(RevokedOutput::build(dumb_point, DelayedPaymentBasepoint::from(dumb_point), HtlcBasepoint::from(dumb_point), dumb_scalar, Amount::ZERO, 0, $is_counterparty_balance_on_anchors))
                        }
                }
        }
index 7a447dd5d9083bc959b9bacca4289715b116ad71..13ecf2539a21de1a4c1f5c2409b1f9619e5bc5f0 100644 (file)
@@ -25,7 +25,7 @@ use bitcoin::blockdata::transaction::Transaction;
 ///
 /// use bitcoin::blockdata::block::Block;
 /// use bitcoin::blockdata::constants::genesis_block;
-/// use bitcoin::network::constants::Network;
+/// use bitcoin::network::Network;
 /// use lightning::chain::transaction::TransactionData;
 ///
 /// let block = genesis_block(Network::Bitcoin);
@@ -88,7 +88,7 @@ impl MaybeSignedTransaction {
 #[cfg(test)]
 mod tests {
        use crate::chain::transaction::OutPoint;
-       use crate::ln::ChannelId;
+       use crate::ln::types::ChannelId;
 
        use bitcoin::blockdata::transaction::Transaction;
        use bitcoin::consensus::encode;
index d6fd3a7dee0013c1bbbfb466737e3fae16a415da..0c51d4562febf12168f9971f4f43d7a94cd875c2 100644 (file)
@@ -12,7 +12,6 @@
 #[cfg(not(fuzzing))]
 mod real_chacha {
        use core::cmp;
-       use core::convert::TryInto;
 
        #[derive(Clone, Copy, PartialEq, Eq)]
        #[allow(non_camel_case_types)]
@@ -335,11 +334,10 @@ pub use self::fuzzy_chacha::ChaCha20;
 
 #[cfg(test)]
 mod test {
-       use alloc::vec;
-       use alloc::vec::{Vec};
-       use core::convert::TryInto;
        use core::iter::repeat;
 
+       use crate::prelude::*;
+
        use super::ChaCha20;
 
        #[test]
index a1b9fbac5160c5ddd8d090e63844f92e6da744ae..593200210055e32605c9ab4822a9f32fd54a9e44 100644 (file)
@@ -8,7 +8,8 @@
 // https://github.com/floodyberry/poly1305-donna
 
 use core::cmp::min;
-use core::convert::TryInto;
+
+use crate::prelude::*;
 
 #[derive(Clone, Copy)]
 pub struct Poly1305 {
@@ -206,7 +207,6 @@ impl Poly1305 {
 #[cfg(test)]
 mod test {
        use core::iter::repeat;
-       use alloc::vec::Vec;
 
        use super::Poly1305;
 
index 14921a386126e1c5c18f36d7f32c0410390dd0aa..e8217b8d9d3999a1feb647fbb1e9d5456bed5ea8 100644 (file)
@@ -151,7 +151,7 @@ mod tests {
                                let writeable_len = $obj.serialized_length() as u64 + 16;
                                let write_adapter = ChaChaPolyWriteAdapter::new(rho, &$obj);
                                let encrypted_writeable_bytes = write_adapter.encode();
-                               let encrypted_writeable = &encrypted_writeable_bytes[..];
+                               let encrypted_writeable = &mut &encrypted_writeable_bytes[..];
 
                                // Now deserialize the object back and make sure it matches the original.
                                let mut rd = FixedLengthReader::new(encrypted_writeable, writeable_len);
index 44f44dd31fc7d488bc420417e26c2c16236c2d72..a1ef44c1bc15e0c46342d4beec870ca3766d339f 100644 (file)
@@ -18,7 +18,7 @@ use crate::chain::chaininterface::{BroadcasterInterface, fee_for_weight};
 use crate::chain::ClaimId;
 use crate::io_extras::sink;
 use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
-use crate::ln::ChannelId;
+use crate::ln::types::ChannelId;
 use crate::ln::chan_utils;
 use crate::ln::chan_utils::{
        ANCHOR_INPUT_WITNESS_WEIGHT, HTLC_SUCCESS_INPUT_ANCHOR_WITNESS_WEIGHT,
@@ -28,24 +28,25 @@ use crate::prelude::*;
 use crate::sign::{
        ChannelDerivationParameters, HTLCDescriptor, SignerProvider, P2WPKH_WITNESS_WEIGHT
 };
-use crate::sign::ecdsa::{EcdsaChannelSigner, WriteableEcdsaChannelSigner};
+use crate::sign::ecdsa::EcdsaChannelSigner;
 use crate::sync::Mutex;
 use crate::util::logger::Logger;
 
-use bitcoin::{OutPoint, PubkeyHash, Sequence, ScriptBuf, Transaction, TxIn, TxOut, Witness, WPubkeyHash};
+use bitcoin::{OutPoint, Psbt, PubkeyHash, Sequence, ScriptBuf, Transaction, TxIn, TxOut, Witness, WPubkeyHash};
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
 use bitcoin::blockdata::locktime::absolute::LockTime;
 use bitcoin::consensus::Encodable;
-use bitcoin::psbt::PartiallySignedTransaction;
 use bitcoin::secp256k1;
 use bitcoin::secp256k1::{PublicKey, Secp256k1};
 use bitcoin::secp256k1::ecdsa::Signature;
+use bitcoin::transaction::Version;
 
-const EMPTY_SCRIPT_SIG_WEIGHT: u64 = 1 /* empty script_sig */ * WITNESS_SCALE_FACTOR as u64;
+pub(crate) const EMPTY_SCRIPT_SIG_WEIGHT: u64 = 1 /* empty script_sig */ * WITNESS_SCALE_FACTOR as u64;
 
 const BASE_INPUT_SIZE: u64 = 32 /* txid */ + 4 /* vout */ + 4 /* sequence */;
 
-const BASE_INPUT_WEIGHT: u64 = BASE_INPUT_SIZE * WITNESS_SCALE_FACTOR as u64;
+pub(crate) const BASE_INPUT_WEIGHT: u64 = BASE_INPUT_SIZE * WITNESS_SCALE_FACTOR as u64;
 
 /// A descriptor used to sign for a commitment transaction's anchor output.
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -62,8 +63,8 @@ impl AnchorDescriptor {
        /// [`Self::unsigned_tx_input`].
        pub fn previous_utxo(&self) -> TxOut {
                TxOut {
-                       script_pubkey: self.witness_script().to_v0_p2wsh(),
-                       value: ANCHOR_OUTPUT_VALUE_SATOSHI,
+                       script_pubkey: self.witness_script().to_p2wsh(),
+                       value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI),
                }
        }
 
@@ -92,7 +93,7 @@ impl AnchorDescriptor {
        }
 
        /// Derives the channel signer required to sign the anchor input.
-       pub fn derive_channel_signer<S: WriteableEcdsaChannelSigner, SP: Deref>(&self, signer_provider: &SP) -> S
+       pub fn derive_channel_signer<S: EcdsaChannelSigner, SP: Deref>(&self, signer_provider: &SP) -> S
        where
                SP::Target: SignerProvider<EcdsaSigner= S>
        {
@@ -257,7 +258,7 @@ pub struct Utxo {
 
 impl Utxo {
        /// Returns a `Utxo` with the `satisfaction_weight` estimate for a legacy P2PKH output.
-       pub fn new_p2pkh(outpoint: OutPoint, value: u64, pubkey_hash: &PubkeyHash) -> Self {
+       pub fn new_p2pkh(outpoint: OutPoint, value: Amount, pubkey_hash: &PubkeyHash) -> Self {
                let script_sig_size = 1 /* script_sig length */ +
                        1 /* OP_PUSH73 */ +
                        73 /* sig including sighash flag */ +
@@ -274,7 +275,7 @@ impl Utxo {
        }
 
        /// Returns a `Utxo` with the `satisfaction_weight` estimate for a P2WPKH nested in P2SH output.
-       pub fn new_nested_p2wpkh(outpoint: OutPoint, value: u64, pubkey_hash: &WPubkeyHash) -> Self {
+       pub fn new_nested_p2wpkh(outpoint: OutPoint, value: Amount, pubkey_hash: &WPubkeyHash) -> Self {
                let script_sig_size = 1 /* script_sig length */ +
                        1 /* OP_0 */ +
                        1 /* OP_PUSH20 */ +
@@ -283,19 +284,19 @@ impl Utxo {
                        outpoint,
                        output: TxOut {
                                value,
-                               script_pubkey: ScriptBuf::new_p2sh(&ScriptBuf::new_v0_p2wpkh(pubkey_hash).script_hash()),
+                               script_pubkey: ScriptBuf::new_p2sh(&ScriptBuf::new_p2wpkh(pubkey_hash).script_hash()),
                        },
                        satisfaction_weight: script_sig_size * WITNESS_SCALE_FACTOR as u64 + P2WPKH_WITNESS_WEIGHT,
                }
        }
 
        /// Returns a `Utxo` with the `satisfaction_weight` estimate for a SegWit v0 P2WPKH output.
-       pub fn new_v0_p2wpkh(outpoint: OutPoint, value: u64, pubkey_hash: &WPubkeyHash) -> Self {
+       pub fn new_v0_p2wpkh(outpoint: OutPoint, value: Amount, pubkey_hash: &WPubkeyHash) -> Self {
                Self {
                        outpoint,
                        output: TxOut {
                                value,
-                               script_pubkey: ScriptBuf::new_v0_p2wpkh(pubkey_hash),
+                               script_pubkey: ScriptBuf::new_p2wpkh(pubkey_hash),
                        },
                        satisfaction_weight: EMPTY_SCRIPT_SIG_WEIGHT + P2WPKH_WITNESS_WEIGHT,
                }
@@ -356,7 +357,7 @@ pub trait CoinSelectionSource {
        ///
        /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
        /// unsigned transaction and then sign it with your wallet.
-       fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()>;
+       fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()>;
 }
 
 /// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
@@ -373,7 +374,7 @@ pub trait WalletSource {
        ///
        /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
        /// unsigned transaction and then sign it with your wallet.
-       fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()>;
+       fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()>;
 }
 
 /// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
@@ -414,7 +415,7 @@ where
        fn select_confirmed_utxos_internal(
                &self, utxos: &[Utxo], claim_id: ClaimId, force_conflicting_utxo_spend: bool,
                tolerate_high_network_feerates: bool, target_feerate_sat_per_1000_weight: u32,
-               preexisting_tx_weight: u64, input_amount_sat: u64, target_amount_sat: u64,
+               preexisting_tx_weight: u64, input_amount_sat: Amount, target_amount_sat: Amount,
        ) -> Result<CoinSelection, ()> {
                let mut locked_utxos = self.locked_utxos.lock().unwrap();
                let mut eligible_utxos = utxos.iter().filter_map(|utxo| {
@@ -424,9 +425,9 @@ where
                                        return None;
                                }
                        }
-                       let fee_to_spend_utxo = fee_for_weight(
+                       let fee_to_spend_utxo = Amount::from_sat(fee_for_weight(
                                target_feerate_sat_per_1000_weight, BASE_INPUT_WEIGHT + utxo.satisfaction_weight,
-                       );
+                       ));
                        let should_spend = if tolerate_high_network_feerates {
                                utxo.output.value > fee_to_spend_utxo
                        } else {
@@ -442,7 +443,7 @@ where
                eligible_utxos.sort_unstable_by_key(|(utxo, _)| utxo.output.value);
 
                let mut selected_amount = input_amount_sat;
-               let mut total_fees = fee_for_weight(target_feerate_sat_per_1000_weight, preexisting_tx_weight);
+               let mut total_fees = Amount::from_sat(fee_for_weight(target_feerate_sat_per_1000_weight, preexisting_tx_weight));
                let mut selected_utxos = Vec::new();
                for (utxo, fee_to_spend_utxo) in eligible_utxos {
                        if selected_amount >= target_amount_sat + total_fees {
@@ -469,8 +470,8 @@ where
                        (8 /* value */ + change_script.consensus_encode(&mut sink()).unwrap() as u64) *
                                WITNESS_SCALE_FACTOR as u64,
                );
-               let change_output_amount = remaining_amount.saturating_sub(change_output_fee);
-               let change_output = if change_output_amount < change_script.dust_value().to_sat() {
+               let change_output_amount = Amount::from_sat(remaining_amount.to_sat().saturating_sub(change_output_fee));
+               let change_output = if change_output_amount < change_script.dust_value() {
                        log_debug!(self.logger, "Coin selection attempt did not yield change output");
                        None
                } else {
@@ -504,7 +505,7 @@ where
 
                let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_input_weight +
                        ((BASE_TX_SIZE + total_output_size) * WITNESS_SCALE_FACTOR as u64);
-               let input_amount_sat: u64 = must_spend.iter().map(|input| input.previous_utxo.value).sum();
+               let input_amount_sat = must_spend.iter().map(|input| input.previous_utxo.value).sum();
                let target_amount_sat = must_pay_to.iter().map(|output| output.value).sum();
                let do_coin_selection = |force_conflicting_utxo_spend: bool, tolerate_high_network_feerates: bool| {
                        log_debug!(self.logger, "Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})",
@@ -520,7 +521,7 @@ where
                        .or_else(|_| do_coin_selection(true, true))
        }
 
-       fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()> {
+       fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
                self.source.sign_psbt(psbt)
        }
 }
@@ -583,7 +584,7 @@ where
                        // way to include a dummy output.
                        log_debug!(self.logger, "Including dummy OP_RETURN output since an output is needed and a change output was not provided");
                        tx.output.push(TxOut {
-                               value: 0,
+                               value: Amount::ZERO,
                                script_pubkey: ScriptBuf::new_op_return(&[]),
                        });
                }
@@ -597,9 +598,10 @@ where
                commitment_tx: &Transaction, commitment_tx_fee_sat: u64, anchor_descriptor: &AnchorDescriptor,
        ) -> Result<(), ()> {
                // Our commitment transaction already has fees allocated to it, so we should take them into
-               // account. We do so by pretending the commitment tranasction's fee and weight are part of
+               // account. We do so by pretending the commitment transaction's fee and weight are part of
                // the anchor input.
                let mut anchor_utxo = anchor_descriptor.previous_utxo();
+               let commitment_tx_fee_sat = Amount::from_sat(commitment_tx_fee_sat);
                anchor_utxo.value += commitment_tx_fee_sat;
                let must_spend = vec![Input {
                        outpoint: anchor_descriptor.outpoint,
@@ -607,16 +609,16 @@ where
                        satisfaction_weight: commitment_tx.weight().to_wu() + ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT,
                }];
                #[cfg(debug_assertions)]
-               let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value).sum::<u64>();
+               let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value).sum::<Amount>();
 
-               log_debug!(self.logger, "Peforming coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW",
+               log_debug!(self.logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW",
                        package_target_feerate_sat_per_1000_weight);
                let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos(
                        claim_id, must_spend, &[], package_target_feerate_sat_per_1000_weight,
                )?;
 
                let mut anchor_tx = Transaction {
-                       version: 2,
+                       version: Version::TWO,
                        lock_time: LockTime::ZERO, // TODO: Use next best height.
                        input: vec![anchor_descriptor.unsigned_tx_input()],
                        output: vec![],
@@ -627,13 +629,13 @@ where
                        coin_selection.confirmed_utxos.iter().map(|utxo| utxo.satisfaction_weight).sum::<u64>();
                #[cfg(debug_assertions)]
                let total_input_amount = must_spend_amount +
-                       coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::<u64>();
+                       coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum();
 
                self.process_coin_selection(&mut anchor_tx, &coin_selection);
                let anchor_txid = anchor_tx.txid();
 
                // construct psbt
-               let mut anchor_psbt = PartiallySignedTransaction::from_unsigned_tx(anchor_tx).unwrap();
+               let mut anchor_psbt = Psbt::from_unsigned_tx(anchor_tx).unwrap();
                // add witness_utxo to anchor input
                anchor_psbt.inputs[0].witness_utxo = Some(anchor_descriptor.previous_utxo());
                // add witness_utxo to remaining inputs
@@ -665,10 +667,10 @@ where
                        assert!(expected_signed_tx_weight >= signed_tx_weight &&
                                expected_signed_tx_weight - (expected_signed_tx_weight / 100) <= signed_tx_weight);
 
-                       let expected_package_fee = fee_for_weight(package_target_feerate_sat_per_1000_weight,
-                               signed_tx_weight + commitment_tx.weight().to_wu());
+                       let expected_package_fee = Amount::from_sat(fee_for_weight(package_target_feerate_sat_per_1000_weight,
+                               signed_tx_weight + commitment_tx.weight().to_wu()));
                        let package_fee = total_input_amount -
-                               anchor_tx.output.iter().map(|output| output.value).sum::<u64>();
+                               anchor_tx.output.iter().map(|output| output.value).sum();
                        // Our fee should be within a 5% error margin of the expected fee based on the
                        // feerate and transaction weight and we should never pay less than required.
                        let fee_error_margin = expected_package_fee * 5 / 100;
@@ -689,7 +691,7 @@ where
                htlc_descriptors: &[HTLCDescriptor], tx_lock_time: LockTime,
        ) -> Result<(), ()> {
                let mut htlc_tx = Transaction {
-                       version: 2,
+                       version: Version::TWO,
                        lock_time: tx_lock_time,
                        input: vec![],
                        output: vec![],
@@ -711,14 +713,14 @@ where
                        htlc_tx.output.push(htlc_output);
                }
 
-               log_debug!(self.logger, "Peforming coin selection for HTLC transaction targeting {} sat/kW",
+               log_debug!(self.logger, "Performing coin selection for HTLC transaction targeting {} sat/kW",
                        target_feerate_sat_per_1000_weight);
 
                #[cfg(debug_assertions)]
                let must_spend_satisfaction_weight =
                        must_spend.iter().map(|input| input.satisfaction_weight).sum::<u64>();
                #[cfg(debug_assertions)]
-               let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value).sum::<u64>();
+               let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value.to_sat()).sum::<u64>();
 
                let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos(
                        claim_id, must_spend, &htlc_tx.output, target_feerate_sat_per_1000_weight,
@@ -729,12 +731,12 @@ where
                        coin_selection.confirmed_utxos.iter().map(|utxo| utxo.satisfaction_weight).sum::<u64>();
                #[cfg(debug_assertions)]
                let total_input_amount = must_spend_amount +
-                       coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::<u64>();
+                       coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value.to_sat()).sum::<u64>();
 
                self.process_coin_selection(&mut htlc_tx, &coin_selection);
 
                // construct psbt
-               let mut htlc_psbt = PartiallySignedTransaction::from_unsigned_tx(htlc_tx).unwrap();
+               let mut htlc_psbt = Psbt::from_unsigned_tx(htlc_tx).unwrap();
                // add witness_utxo to htlc inputs
                for (i, htlc_descriptor) in htlc_descriptors.iter().enumerate() {
                        debug_assert_eq!(htlc_psbt.unsigned_tx.input[i].previous_output, htlc_descriptor.outpoint());
@@ -775,7 +777,7 @@ where
 
                        let expected_signed_tx_fee = fee_for_weight(target_feerate_sat_per_1000_weight, signed_tx_weight);
                        let signed_tx_fee = total_input_amount -
-                               htlc_tx.output.iter().map(|output| output.value).sum::<u64>();
+                               htlc_tx.output.iter().map(|output| output.value.to_sat()).sum::<u64>();
                        // Our fee should be within a 5% error margin of the expected fee based on the
                        // feerate and transaction weight and we should never pay less than required.
                        let fee_error_margin = expected_signed_tx_fee * 5 / 100;
index 485a23e0292e520c28d47a226fe7de51abdd9690..45ed895ade7a0f9010c07a3bc7de73e1e1dd6913 100644 (file)
@@ -18,12 +18,13 @@ pub mod bump_transaction;
 
 pub use bump_transaction::BumpTransactionEvent;
 
+use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef};
 use crate::sign::SpendableOutputDescriptor;
 use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields};
 use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
 use crate::ln::features::ChannelTypeFeatures;
 use crate::ln::msgs;
-use crate::ln::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
+use crate::ln::types::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
 use crate::chain::transaction;
 use crate::routing::gossip::NetworkUpdate;
 use crate::util::errors::APIError;
@@ -37,21 +38,25 @@ use bitcoin::blockdata::script::ScriptBuf;
 use bitcoin::hashes::Hash;
 use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::secp256k1::PublicKey;
+use bitcoin::transaction::Version;
 use crate::io;
-use crate::prelude::*;
 use core::time::Duration;
 use core::ops::Deref;
 use crate::sync::Arc;
 
+#[allow(unused_imports)]
+use crate::prelude::*;
+
 /// Some information provided on receipt of payment depends on whether the payment received is a
 /// spontaneous payment or a "conventional" lightning payment that's paying an invoice.
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum PaymentPurpose {
-       /// Information for receiving a payment that we generated an invoice for.
-       InvoicePayment {
+       /// A payment for a BOLT 11 invoice.
+       Bolt11InvoicePayment {
                /// The preimage to the payment_hash, if the payment hash (and secret) were fetched via
-               /// [`ChannelManager::create_inbound_payment`]. If provided, this can be handed directly to
-               /// [`ChannelManager::claim_funds`].
+               /// [`ChannelManager::create_inbound_payment`]. When handling [`Event::PaymentClaimable`],
+               /// this can be passed directly to [`ChannelManager::claim_funds`] to claim the payment. No
+               /// action is needed when seen in [`Event::PaymentClaimed`].
                ///
                /// [`ChannelManager::create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment
                /// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
@@ -68,6 +73,48 @@ pub enum PaymentPurpose {
                /// [`ChannelManager::create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
                payment_secret: PaymentSecret,
        },
+       /// A payment for a BOLT 12 [`Offer`].
+       ///
+       /// [`Offer`]: crate::offers::offer::Offer
+       Bolt12OfferPayment {
+               /// The preimage to the payment hash. When handling [`Event::PaymentClaimable`], this can be
+               /// passed directly to [`ChannelManager::claim_funds`], if provided. No action is needed
+               /// when seen in [`Event::PaymentClaimed`].
+               ///
+               /// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
+               payment_preimage: Option<PaymentPreimage>,
+               /// The secret used to authenticate the sender to the recipient, preventing a number of
+               /// de-anonymization attacks while routing a payment.
+               ///
+               /// See [`PaymentPurpose::Bolt11InvoicePayment::payment_secret`] for further details.
+               payment_secret: PaymentSecret,
+               /// The context of the payment such as information about the corresponding [`Offer`] and
+               /// [`InvoiceRequest`].
+               ///
+               /// [`Offer`]: crate::offers::offer::Offer
+               /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+               payment_context: Bolt12OfferContext,
+       },
+       /// A payment for a BOLT 12 [`Refund`].
+       ///
+       /// [`Refund`]: crate::offers::refund::Refund
+       Bolt12RefundPayment {
+               /// The preimage to the payment hash. When handling [`Event::PaymentClaimable`], this can be
+               /// passed directly to [`ChannelManager::claim_funds`], if provided. No action is needed
+               /// when seen in [`Event::PaymentClaimed`].
+               ///
+               /// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
+               payment_preimage: Option<PaymentPreimage>,
+               /// The secret used to authenticate the sender to the recipient, preventing a number of
+               /// de-anonymization attacks while routing a payment.
+               ///
+               /// See [`PaymentPurpose::Bolt11InvoicePayment::payment_secret`] for further details.
+               payment_secret: PaymentSecret,
+               /// The context of the payment such as information about the corresponding [`Refund`].
+               ///
+               /// [`Refund`]: crate::offers::refund::Refund
+               payment_context: Bolt12RefundContext,
+       },
        /// Because this is a spontaneous payment, the payer generated their own preimage rather than us
        /// (the payee) providing a preimage.
        SpontaneousPayment(PaymentPreimage),
@@ -77,17 +124,67 @@ impl PaymentPurpose {
        /// Returns the preimage for this payment, if it is known.
        pub fn preimage(&self) -> Option<PaymentPreimage> {
                match self {
-                       PaymentPurpose::InvoicePayment { payment_preimage, .. } => *payment_preimage,
+                       PaymentPurpose::Bolt11InvoicePayment { payment_preimage, .. } => *payment_preimage,
+                       PaymentPurpose::Bolt12OfferPayment { payment_preimage, .. } => *payment_preimage,
+                       PaymentPurpose::Bolt12RefundPayment { payment_preimage, .. } => *payment_preimage,
                        PaymentPurpose::SpontaneousPayment(preimage) => Some(*preimage),
                }
        }
+
+       pub(crate) fn is_keysend(&self) -> bool {
+               match self {
+                       PaymentPurpose::Bolt11InvoicePayment { .. } => false,
+                       PaymentPurpose::Bolt12OfferPayment { .. } => false,
+                       PaymentPurpose::Bolt12RefundPayment { .. } => false,
+                       PaymentPurpose::SpontaneousPayment(..) => true,
+               }
+       }
+
+       pub(crate) fn from_parts(
+               payment_preimage: Option<PaymentPreimage>, payment_secret: PaymentSecret,
+               payment_context: Option<PaymentContext>,
+       ) -> Self {
+               match payment_context {
+                       Some(PaymentContext::Unknown(_)) | None => {
+                               PaymentPurpose::Bolt11InvoicePayment {
+                                       payment_preimage,
+                                       payment_secret,
+                               }
+                       },
+                       Some(PaymentContext::Bolt12Offer(context)) => {
+                               PaymentPurpose::Bolt12OfferPayment {
+                                       payment_preimage,
+                                       payment_secret,
+                                       payment_context: context,
+                               }
+                       },
+                       Some(PaymentContext::Bolt12Refund(context)) => {
+                               PaymentPurpose::Bolt12RefundPayment {
+                                       payment_preimage,
+                                       payment_secret,
+                                       payment_context: context,
+                               }
+                       },
+               }
+       }
 }
 
 impl_writeable_tlv_based_enum!(PaymentPurpose,
-       (0, InvoicePayment) => {
+       (0, Bolt11InvoicePayment) => {
                (0, payment_preimage, option),
                (2, payment_secret, required),
-       };
+       },
+       (4, Bolt12OfferPayment) => {
+               (0, payment_preimage, option),
+               (2, payment_secret, required),
+               (4, payment_context, required),
+       },
+       (6, Bolt12RefundPayment) => {
+               (0, payment_preimage, option),
+               (2, payment_secret, required),
+               (4, payment_context, required),
+       },
+       ;
        (2, SpontaneousPayment)
 );
 
@@ -232,6 +329,8 @@ pub enum ClosureReason {
        /// Another channel in the same funding batch closed before the funding transaction
        /// was ready to be broadcast.
        FundingBatchClosure,
+       /// One of our HTLCs timed out in a channel, causing us to force close the channel.
+       HTLCsTimedOut,
 }
 
 impl core::fmt::Display for ClosureReason {
@@ -241,7 +340,7 @@ impl core::fmt::Display for ClosureReason {
                        ClosureReason::CounterpartyForceClosed { peer_msg } => {
                                f.write_fmt(format_args!("counterparty force-closed with message: {}", peer_msg))
                        },
-                       ClosureReason::HolderForceClosed => f.write_str("user manually force-closed the channel"),
+                       ClosureReason::HolderForceClosed => f.write_str("user force-closed the channel"),
                        ClosureReason::LegacyCooperativeClosure => f.write_str("the channel was cooperatively closed"),
                        ClosureReason::CounterpartyInitiatedCooperativeClosure => f.write_str("the channel was cooperatively closed by our peer"),
                        ClosureReason::LocallyInitiatedCooperativeClosure => f.write_str("the channel was cooperatively closed by us"),
@@ -255,6 +354,7 @@ impl core::fmt::Display for ClosureReason {
                        ClosureReason::OutdatedChannelManager => f.write_str("the ChannelManager read from disk was stale compared to ChannelMonitor(s)"),
                        ClosureReason::CounterpartyCoopClosedUnfundedChannel => f.write_str("the peer requested the unfunded channel be closed"),
                        ClosureReason::FundingBatchClosure => f.write_str("another channel in the same funding batch closed"),
+                       ClosureReason::HTLCsTimedOut => f.write_str("htlcs on the channel timed out"),
                }
        }
 }
@@ -272,6 +372,7 @@ impl_writeable_tlv_based_enum_upgradable!(ClosureReason,
        (15, FundingBatchClosure) => {},
        (17, CounterpartyInitiatedCooperativeClosure) => {},
        (19, LocallyInitiatedCooperativeClosure) => {},
+       (21, HTLCsTimedOut) => {},
 );
 
 /// Intended destination of a failed HTLC as indicated in [`Event::HTLCHandlingFailed`].
@@ -298,6 +399,8 @@ pub enum HTLCDestination {
                /// Short channel id we are requesting to forward an HTLC to.
                requested_forward_scid: u64
        },
+       /// We couldn't decode the incoming onion to obtain the forwarding details.
+       InvalidOnion,
        /// Failure scenario where an HTLC may have been forwarded to be intended for us,
        /// but is invalid for some reason, so we reject it.
        ///
@@ -325,6 +428,7 @@ impl_writeable_tlv_based_enum_upgradable!(HTLCDestination,
        (2, UnknownNextHop) => {
                (0, requested_forward_scid, required),
        },
+       (3, InvalidOnion) => {},
        (4, FailedPayment) => {
                (0, payment_hash, required),
        },
@@ -550,6 +654,11 @@ pub enum Event {
                /// The sender-intended sum total of all the MPP parts. This will be `None` for events
                /// serialized prior to LDK version 0.0.117.
                sender_intended_total_msat: Option<u64>,
+               /// The fields in the onion which were received with each HTLC. Only fields which were
+               /// identical in each HTLC involved in the payment will be included here.
+               ///
+               /// Payments received on LDK versions prior to 0.0.124 will have this field unset.
+               onion_fields: Option<RecipientOnionFields>,
        },
        /// Indicates that a peer connection with a node is needed in order to send an [`OnionMessage`].
        ///
@@ -783,9 +892,15 @@ pub enum Event {
        },
        /// Used to indicate that an output which you should know how to spend was confirmed on chain
        /// and is now spendable.
-       /// Such an output will *not* ever be spent by rust-lightning, and are not at risk of your
+       ///
+       /// Such an output will *never* be spent directly by LDK, and are not at risk of your
        /// counterparty spending them due to some kind of timeout. Thus, you need to store them
        /// somewhere and spend them when you create on-chain transactions.
+       ///
+       /// You may hand them to the [`OutputSweeper`] utility which will store and (re-)generate spending
+       /// transactions for you.
+       ///
+       /// [`OutputSweeper`]: crate::util::sweep::OutputSweeper
        SpendableOutputs {
                /// The outputs which you should store as spendable by you.
                outputs: Vec<SpendableOutputDescriptor>,
@@ -797,12 +912,24 @@ pub enum Event {
        /// This event is generated when a payment has been successfully forwarded through us and a
        /// forwarding fee earned.
        PaymentForwarded {
-               /// The incoming channel between the previous node and us. This is only `None` for events
-               /// generated or serialized by versions prior to 0.0.107.
+               /// The channel id of the incoming channel between the previous node and us.
+               ///
+               /// This is only `None` for events generated or serialized by versions prior to 0.0.107.
                prev_channel_id: Option<ChannelId>,
-               /// The outgoing channel between the next node and us. This is only `None` for events
-               /// generated or serialized by versions prior to 0.0.107.
+               /// The channel id of the outgoing channel between the next node and us.
+               ///
+               /// This is only `None` for events generated or serialized by versions prior to 0.0.107.
                next_channel_id: Option<ChannelId>,
+               /// The `user_channel_id` of the incoming channel between the previous node and us.
+               ///
+               /// This is only `None` for events generated or serialized by versions prior to 0.0.122.
+               prev_user_channel_id: Option<u128>,
+               /// The `user_channel_id` of the outgoing channel between the next node and us.
+               ///
+               /// This will be `None` if the payment was settled via an on-chain transaction. See the
+               /// caveat described for the `total_fee_earned_msat` field. Moreover it will be `None` for
+               /// events generated or serialized by versions prior to 0.0.122.
+               next_user_channel_id: Option<u128>,
                /// The total fee, in milli-satoshis, which was earned as a result of the payment.
                ///
                /// Note that if we force-closed the channel over which we forwarded an HTLC while the HTLC
@@ -891,8 +1018,8 @@ pub enum Event {
                /// The features that this channel will operate with.
                channel_type: ChannelTypeFeatures,
        },
-       /// Used to indicate that a previously opened channel with the given `channel_id` is in the
-       /// process of closure.
+       /// Used to indicate that a channel that got past the initial handshake with the given `channel_id` is in the
+       /// process of closure. This includes previously opened channels, and channels that time out from not being funded.
        ///
        /// Note that this event is only triggered for accepted channels: if the
        /// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true and the channel is
@@ -1020,6 +1147,30 @@ pub enum Event {
        ///
        /// [`ChannelHandshakeConfig::negotiate_anchors_zero_fee_htlc_tx`]: crate::util::config::ChannelHandshakeConfig::negotiate_anchors_zero_fee_htlc_tx
        BumpTransaction(BumpTransactionEvent),
+       /// We received an onion message that is intended to be forwarded to a peer
+       /// that is currently offline. This event will only be generated if the
+       /// `OnionMessenger` was initialized with
+       /// [`OnionMessenger::new_with_offline_peer_interception`], see its docs.
+       ///
+       /// [`OnionMessenger::new_with_offline_peer_interception`]: crate::onion_message::messenger::OnionMessenger::new_with_offline_peer_interception
+       OnionMessageIntercepted {
+               /// The node id of the offline peer.
+               peer_node_id: PublicKey,
+               /// The onion message intended to be forwarded to `peer_node_id`.
+               message: msgs::OnionMessage,
+       },
+       /// Indicates that an onion message supporting peer has come online and it may
+       /// be time to forward any onion messages that were previously intercepted for
+       /// them. This event will only be generated if the `OnionMessenger` was
+       /// initialized with
+       /// [`OnionMessenger::new_with_offline_peer_interception`], see its docs.
+       ///
+       /// [`OnionMessenger::new_with_offline_peer_interception`]: crate::onion_message::messenger::OnionMessenger::new_with_offline_peer_interception
+       OnionMessagePeerConnected {
+               /// The node id of the peer we just connected to, who advertises support for
+               /// onion messages.
+               peer_node_id: PublicKey,
+       }
 }
 
 impl Writeable for Event {
@@ -1037,11 +1188,28 @@ impl Writeable for Event {
                                1u8.write(writer)?;
                                let mut payment_secret = None;
                                let payment_preimage;
+                               let mut payment_context = None;
                                match &purpose {
-                                       PaymentPurpose::InvoicePayment { payment_preimage: preimage, payment_secret: secret } => {
+                                       PaymentPurpose::Bolt11InvoicePayment {
+                                               payment_preimage: preimage, payment_secret: secret
+                                       } => {
                                                payment_secret = Some(secret);
                                                payment_preimage = *preimage;
                                        },
+                                       PaymentPurpose::Bolt12OfferPayment {
+                                               payment_preimage: preimage, payment_secret: secret, payment_context: context
+                                       } => {
+                                               payment_secret = Some(secret);
+                                               payment_preimage = *preimage;
+                                               payment_context = Some(PaymentContextRef::Bolt12Offer(context));
+                                       },
+                                       PaymentPurpose::Bolt12RefundPayment {
+                                               payment_preimage: preimage, payment_secret: secret, payment_context: context
+                                       } => {
+                                               payment_secret = Some(secret);
+                                               payment_preimage = *preimage;
+                                               payment_context = Some(PaymentContextRef::Bolt12Refund(context));
+                                       },
                                        PaymentPurpose::SpontaneousPayment(preimage) => {
                                                payment_preimage = Some(*preimage);
                                        }
@@ -1060,6 +1228,7 @@ impl Writeable for Event {
                                        (8, payment_preimage, option),
                                        (9, onion_fields, option),
                                        (10, skimmed_fee_opt, option),
+                                       (11, payment_context, option),
                                });
                        },
                        &Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref fee_paid_msat } => {
@@ -1121,8 +1290,9 @@ impl Writeable for Event {
                                });
                        }
                        &Event::PaymentForwarded {
-                               total_fee_earned_msat, prev_channel_id, claim_from_onchain_tx,
-                               next_channel_id, outbound_amount_forwarded_msat, skimmed_fee_msat,
+                               prev_channel_id, next_channel_id, prev_user_channel_id, next_user_channel_id,
+                               total_fee_earned_msat, skimmed_fee_msat, claim_from_onchain_tx,
+                               outbound_amount_forwarded_msat,
                        } => {
                                7u8.write(writer)?;
                                write_tlv_fields!(writer, {
@@ -1132,6 +1302,8 @@ impl Writeable for Event {
                                        (3, next_channel_id, option),
                                        (5, outbound_amount_forwarded_msat, option),
                                        (7, skimmed_fee_msat, option),
+                                       (9, prev_user_channel_id, option),
+                                       (11, next_user_channel_id, option),
                                });
                        },
                        &Event::ChannelClosed { ref channel_id, ref user_channel_id, ref reason,
@@ -1182,7 +1354,7 @@ impl Writeable for Event {
                                // We never write the OpenChannelRequest events as, upon disconnection, peers
                                // drop any channels which have not yet exchanged funding_signed.
                        },
-                       &Event::PaymentClaimed { ref payment_hash, ref amount_msat, ref purpose, ref receiver_node_id, ref htlcs, ref sender_intended_total_msat } => {
+                       &Event::PaymentClaimed { ref payment_hash, ref amount_msat, ref purpose, ref receiver_node_id, ref htlcs, ref sender_intended_total_msat, ref onion_fields } => {
                                19u8.write(writer)?;
                                write_tlv_fields!(writer, {
                                        (0, payment_hash, required),
@@ -1191,6 +1363,7 @@ impl Writeable for Event {
                                        (4, amount_msat, required),
                                        (5, *htlcs, optional_vec),
                                        (7, sender_intended_total_msat, option),
+                                       (9, onion_fields, option),
                                });
                        },
                        &Event::ProbeSuccessful { ref payment_id, ref payment_hash, ref path } => {
@@ -1262,6 +1435,19 @@ impl Writeable for Event {
                                35u8.write(writer)?;
                                // Never write ConnectionNeeded events as buffered onion messages aren't serialized.
                        },
+                       &Event::OnionMessageIntercepted { ref peer_node_id, ref message } => {
+                               37u8.write(writer)?;
+                               write_tlv_fields!(writer, {
+                                       (0, peer_node_id, required),
+                                       (2, message, required),
+                               });
+                       },
+                       &Event::OnionMessagePeerConnected { ref peer_node_id } => {
+                               39u8.write(writer)?;
+                               write_tlv_fields!(writer, {
+                                       (0, peer_node_id, required),
+                               });
+                       }
                        // Note that, going forward, all new events must only write data inside of
                        // `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
                        // data via `write_tlv_fields`.
@@ -1275,7 +1461,7 @@ impl MaybeReadable for Event {
                        // Note that we do not write a length-prefixed TLV for FundingGenerationReady events.
                        0u8 => Ok(None),
                        1u8 => {
-                               let f = || {
+                               let mut f = || {
                                        let mut payment_hash = PaymentHash([0; 32]);
                                        let mut payment_preimage = None;
                                        let mut payment_secret = None;
@@ -1287,6 +1473,7 @@ impl MaybeReadable for Event {
                                        let mut claim_deadline = None;
                                        let mut via_user_channel_id = None;
                                        let mut onion_fields = None;
+                                       let mut payment_context = None;
                                        read_tlv_fields!(reader, {
                                                (0, payment_hash, required),
                                                (1, receiver_node_id, option),
@@ -1299,12 +1486,10 @@ impl MaybeReadable for Event {
                                                (8, payment_preimage, option),
                                                (9, onion_fields, option),
                                                (10, counterparty_skimmed_fee_msat_opt, option),
+                                               (11, payment_context, option),
                                        });
                                        let purpose = match payment_secret {
-                                               Some(secret) => PaymentPurpose::InvoicePayment {
-                                                       payment_preimage,
-                                                       payment_secret: secret
-                                               },
+                                               Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context),
                                                None if payment_preimage.is_some() => PaymentPurpose::SpontaneousPayment(payment_preimage.unwrap()),
                                                None => return Err(msgs::DecodeError::InvalidValue),
                                        };
@@ -1323,7 +1508,7 @@ impl MaybeReadable for Event {
                                f()
                        },
                        2u8 => {
-                               let f = || {
+                               let mut f = || {
                                        let mut payment_preimage = PaymentPreimage([0; 32]);
                                        let mut payment_hash = None;
                                        let mut payment_id = None;
@@ -1347,7 +1532,7 @@ impl MaybeReadable for Event {
                                f()
                        },
                        3u8 => {
-                               let f = || {
+                               let mut f = || {
                                        #[cfg(test)]
                                        let error_code = Readable::read(reader)?;
                                        #[cfg(test)]
@@ -1390,7 +1575,7 @@ impl MaybeReadable for Event {
                        },
                        4u8 => Ok(None),
                        5u8 => {
-                               let f = || {
+                               let mut f = || {
                                        let mut outputs = WithoutLength(Vec::new());
                                        let mut channel_id: Option<ChannelId> = None;
                                        read_tlv_fields!(reader, {
@@ -1426,13 +1611,15 @@ impl MaybeReadable for Event {
                                }))
                        },
                        7u8 => {
-                               let f = || {
-                                       let mut total_fee_earned_msat = None;
+                               let mut f = || {
                                        let mut prev_channel_id = None;
-                                       let mut claim_from_onchain_tx = false;
                                        let mut next_channel_id = None;
-                                       let mut outbound_amount_forwarded_msat = None;
+                                       let mut prev_user_channel_id = None;
+                                       let mut next_user_channel_id = None;
+                                       let mut total_fee_earned_msat = None;
                                        let mut skimmed_fee_msat = None;
+                                       let mut claim_from_onchain_tx = false;
+                                       let mut outbound_amount_forwarded_msat = None;
                                        read_tlv_fields!(reader, {
                                                (0, total_fee_earned_msat, option),
                                                (1, prev_channel_id, option),
@@ -1440,16 +1627,19 @@ impl MaybeReadable for Event {
                                                (3, next_channel_id, option),
                                                (5, outbound_amount_forwarded_msat, option),
                                                (7, skimmed_fee_msat, option),
+                                               (9, prev_user_channel_id, option),
+                                               (11, next_user_channel_id, option),
                                        });
                                        Ok(Some(Event::PaymentForwarded {
-                                               total_fee_earned_msat, prev_channel_id, claim_from_onchain_tx, next_channel_id,
-                                               outbound_amount_forwarded_msat, skimmed_fee_msat,
+                                               prev_channel_id, next_channel_id, prev_user_channel_id,
+                                               next_user_channel_id, total_fee_earned_msat, skimmed_fee_msat,
+                                               claim_from_onchain_tx, outbound_amount_forwarded_msat,
                                        }))
                                };
                                f()
                        },
                        9u8 => {
-                               let f = || {
+                               let mut f = || {
                                        let mut channel_id = ChannelId::new_zero();
                                        let mut reason = UpgradableRequired(None);
                                        let mut user_channel_id_low_opt: Option<u64> = None;
@@ -1479,9 +1669,9 @@ impl MaybeReadable for Event {
                                f()
                        },
                        11u8 => {
-                               let f = || {
+                               let mut f = || {
                                        let mut channel_id = ChannelId::new_zero();
-                                       let mut transaction = Transaction{ version: 2, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
+                                       let mut transaction = Transaction{ version: Version::TWO, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
                                        read_tlv_fields!(reader, {
                                                (0, channel_id, required),
                                                (2, transaction, required),
@@ -1491,7 +1681,7 @@ impl MaybeReadable for Event {
                                f()
                        },
                        13u8 => {
-                               let f = || {
+                               let mut f = || {
                                        _init_and_read_len_prefixed_tlv_fields!(reader, {
                                                (0, payment_id, required),
                                                (2, payment_hash, option),
@@ -1507,7 +1697,7 @@ impl MaybeReadable for Event {
                                f()
                        },
                        15u8 => {
-                               let f = || {
+                               let mut f = || {
                                        let mut payment_hash = PaymentHash([0; 32]);
                                        let mut payment_id = PaymentId([0; 32]);
                                        let mut reason = None;
@@ -1529,13 +1719,14 @@ impl MaybeReadable for Event {
                                Ok(None)
                        },
                        19u8 => {
-                               let f = || {
+                               let mut f = || {
                                        let mut payment_hash = PaymentHash([0; 32]);
                                        let mut purpose = UpgradableRequired(None);
                                        let mut amount_msat = 0;
                                        let mut receiver_node_id = None;
                                        let mut htlcs: Option<Vec<ClaimedHTLC>> = Some(vec![]);
                                        let mut sender_intended_total_msat: Option<u64> = None;
+                                       let mut onion_fields = None;
                                        read_tlv_fields!(reader, {
                                                (0, payment_hash, required),
                                                (1, receiver_node_id, option),
@@ -1543,6 +1734,7 @@ impl MaybeReadable for Event {
                                                (4, amount_msat, required),
                                                (5, htlcs, optional_vec),
                                                (7, sender_intended_total_msat, option),
+                                               (9, onion_fields, option),
                                        });
                                        Ok(Some(Event::PaymentClaimed {
                                                receiver_node_id,
@@ -1551,12 +1743,13 @@ impl MaybeReadable for Event {
                                                amount_msat,
                                                htlcs: htlcs.unwrap_or(vec![]),
                                                sender_intended_total_msat,
+                                               onion_fields,
                                        }))
                                };
                                f()
                        },
                        21u8 => {
-                               let f = || {
+                               let mut f = || {
                                        _init_and_read_len_prefixed_tlv_fields!(reader, {
                                                (0, payment_id, required),
                                                (2, payment_hash, required),
@@ -1572,7 +1765,7 @@ impl MaybeReadable for Event {
                                f()
                        },
                        23u8 => {
-                               let f = || {
+                               let mut f = || {
                                        _init_and_read_len_prefixed_tlv_fields!(reader, {
                                                (0, payment_id, required),
                                                (2, payment_hash, required),
@@ -1590,7 +1783,7 @@ impl MaybeReadable for Event {
                                f()
                        },
                        25u8 => {
-                               let f = || {
+                               let mut f = || {
                                        let mut prev_channel_id = ChannelId::new_zero();
                                        let mut failed_next_destination_opt = UpgradableRequired(None);
                                        read_tlv_fields!(reader, {
@@ -1606,7 +1799,7 @@ impl MaybeReadable for Event {
                        },
                        27u8 => Ok(None),
                        29u8 => {
-                               let f = || {
+                               let mut f = || {
                                        let mut channel_id = ChannelId::new_zero();
                                        let mut user_channel_id: u128 = 0;
                                        let mut counterparty_node_id = RequiredWrapper(None);
@@ -1628,7 +1821,7 @@ impl MaybeReadable for Event {
                                f()
                        },
                        31u8 => {
-                               let f = || {
+                               let mut f = || {
                                        let mut channel_id = ChannelId::new_zero();
                                        let mut user_channel_id: u128 = 0;
                                        let mut former_temporary_channel_id = None;
@@ -1656,7 +1849,7 @@ impl MaybeReadable for Event {
                                f()
                        },
                        33u8 => {
-                               let f = || {
+                               let mut f = || {
                                        _init_and_read_len_prefixed_tlv_fields!(reader, {
                                                (0, payment_id, required),
                                        });
@@ -1668,6 +1861,29 @@ impl MaybeReadable for Event {
                        },
                        // Note that we do not write a length-prefixed TLV for ConnectionNeeded events.
                        35u8 => Ok(None),
+                       37u8 => {
+                               let mut f = || {
+                                       _init_and_read_len_prefixed_tlv_fields!(reader, {
+                                               (0, peer_node_id, required),
+                                               (2, message, required),
+                                       });
+                                       Ok(Some(Event::OnionMessageIntercepted {
+                                               peer_node_id: peer_node_id.0.unwrap(), message: message.0.unwrap()
+                                       }))
+                               };
+                               f()
+                       },
+                       39u8 => {
+                               let mut f = || {
+                                       _init_and_read_len_prefixed_tlv_fields!(reader, {
+                                               (0, peer_node_id, required),
+                                       });
+                                       Ok(Some(Event::OnionMessagePeerConnected {
+                                               peer_node_id: peer_node_id.0.unwrap()
+                                       }))
+                               };
+                               f()
+                       },
                        // Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
                        // Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
                        // reads.
index 1adf3786b76d48a571665e73e2e8a551276a73f8..5274ea0bf309ce2d0709936d496d8a098b561ebc 100644 (file)
@@ -61,7 +61,7 @@ compile_error!("Tests will always fail with cfg=fuzzing");
 
 #[macro_use]
 extern crate alloc;
-extern crate bitcoin;
+pub extern crate bitcoin;
 #[cfg(any(test, feature = "std"))]
 extern crate core;
 
@@ -94,7 +94,11 @@ pub use std::io;
 pub use core2::io;
 
 #[cfg(not(feature = "std"))]
-mod io_extras {
+#[doc(hidden)]
+/// IO utilities public only for use by in-crate macros. These should not be used externally
+///
+/// This is not exported to bindings users as it is not intended for public consumption.
+pub mod io_extras {
        use core2::io::{self, Read, Write};
 
        /// A writer which will move data into the void.
@@ -154,6 +158,10 @@ mod io_extras {
 }
 
 #[cfg(feature = "std")]
+#[doc(hidden)]
+/// IO utilities public only for use by in-crate macros. These should not be used externally
+///
+/// This is not exported to bindings users as it is not intended for public consumption.
 mod io_extras {
        pub fn read_to_end<D: ::std::io::Read>(mut d: D) -> Result<Vec<u8>, ::std::io::Error> {
                let mut buf = Vec::new();
@@ -165,11 +173,17 @@ mod io_extras {
 }
 
 mod prelude {
+       #![allow(unused_imports)]
+
        pub use alloc::{vec, vec::Vec, string::String, collections::VecDeque, boxed::Box};
 
        pub use alloc::borrow::ToOwned;
        pub use alloc::string::ToString;
 
+       pub use core::convert::{AsMut, AsRef, TryFrom, TryInto};
+       pub use core::default::Default;
+       pub use core::marker::Sized;
+
        pub(crate) use crate::util::hash_tables::*;
 }
 
index 613df570d4e3bf40051d25e59a5d6fa45823dbdb..fbcf13fb6de00c2705acae0ed007edbf45bb4add 100644 (file)
@@ -12,6 +12,7 @@
 
 use bitcoin::{Transaction, TxOut, TxIn, Amount};
 use bitcoin::blockdata::locktime::absolute::LockTime;
+use bitcoin::transaction::Version;
 
 use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS;
 use crate::events::bump_transaction::WalletSource;
@@ -344,12 +345,12 @@ fn do_test_async_holder_signatures(anchors: bool, remote_commitment: bool) {
 
        let closing_node = if remote_commitment { &nodes[1] } else { &nodes[0] };
        let coinbase_tx = Transaction {
-               version: 2,
+               version: Version::TWO,
                lock_time: LockTime::ZERO,
                input: vec![TxIn { ..Default::default() }],
                output: vec![
                        TxOut {
-                               value: Amount::ONE_BTC.to_sat(),
+                               value: Amount::ONE_BTC,
                                script_pubkey: closing_node.wallet_source.get_change_script().unwrap(),
                        },
                ],
index 7adac5472b543c911352bb068b0cb1f59247aebf..03c0e49fb4682899cef605c2a016491701b36edd 100644 (file)
@@ -9,9 +9,9 @@
 
 use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
 use crate::blinded_path::BlindedPath;
-use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs};
+use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentRelay, ReceiveTlvs};
 use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PaymentFailureReason};
-use crate::ln::PaymentSecret;
+use crate::ln::types::PaymentSecret;
 use crate::ln::channelmanager;
 use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
 use crate::ln::features::BlindedHopFeatures;
@@ -63,6 +63,7 @@ fn blinded_payment_path(
                        htlc_minimum_msat:
                                intro_node_min_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_minimum_msat),
                },
+               payment_context: PaymentContext::unknown(),
        };
        let mut secp_ctx = Secp256k1::new();
        BlindedPath::new_for_payment(
@@ -108,6 +109,7 @@ fn do_one_hop_blinded_path(success: bool) {
                        max_cltv_expiry: u32::max_value(),
                        htlc_minimum_msat: chan_upd.htlc_minimum_msat,
                },
+               payment_context: PaymentContext::unknown(),
        };
        let mut secp_ctx = Secp256k1::new();
        let blinded_path = BlindedPath::one_hop_for_payment(
@@ -151,6 +153,7 @@ fn mpp_to_one_hop_blinded_path() {
                        max_cltv_expiry: u32::max_value(),
                        htlc_minimum_msat: chan_upd_1_3.htlc_minimum_msat,
                },
+               payment_context: PaymentContext::unknown(),
        };
        let blinded_path = BlindedPath::one_hop_for_payment(
                nodes[3].node.get_our_node_id(), payee_tlvs, TEST_FINAL_CLTV as u16,
@@ -177,7 +180,9 @@ fn mpp_to_one_hop_blinded_path() {
        let ev = remove_first_msg_event_to_node(&nodes[2].node.get_our_node_id(), &mut events);
        pass_along_path(&nodes[0], expected_route[1], amt_msat, payment_hash.clone(),
                Some(payment_secret), ev.clone(), true, None);
-       claim_payment_along_route(&nodes[0], expected_route, false, payment_preimage);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], expected_route, payment_preimage)
+       );
 }
 
 #[test]
@@ -241,7 +246,9 @@ fn mpp_to_three_hop_blinded_paths() {
        let ev = remove_first_msg_event_to_node(&nodes[2].node.get_our_node_id(), &mut events);
        pass_along_path(&nodes[0], expected_route[1], amt_msat, payment_hash.clone(),
                Some(payment_secret), ev.clone(), true, None);
-       claim_payment_along_route(&nodes[0], expected_route, false, payment_preimage);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], expected_route, payment_preimage)
+       );
 }
 
 enum ForwardCheckFail {
@@ -279,9 +286,10 @@ fn do_forward_checks_failure(check: ForwardCheckFail, intro_fails: bool) {
 
        let amt_msat = 5000;
        let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[3], Some(amt_msat), None);
-       let route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
+       let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
                nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(),
                &[&chan_upd_1_2, &chan_upd_2_3], &chanmon_cfgs[3].keys_manager);
+       route_params.payment_params.max_path_length = 18;
 
        let route = get_route(&nodes[0], &route_params).unwrap();
        node_cfgs[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
@@ -295,11 +303,12 @@ fn do_forward_checks_failure(check: ForwardCheckFail, intro_fails: bool) {
                                        $update_add.cltv_expiry = 10; // causes outbound CLTV expiry to underflow
                                },
                                ForwardCheckFail::ForwardPayloadEncodedAsReceive => {
+                                       let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
                                        let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
                                        let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
                                        let cur_height = nodes[0].best_block_info().1;
                                        let (mut onion_payloads, ..) = onion_utils::build_onion_payloads(
-                                               &route.paths[0], amt_msat, RecipientOnionFields::spontaneous_empty(), cur_height, &None).unwrap();
+                                               &route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None).unwrap();
                                        // Remove the receive payload so the blinded forward payload is encoded as a final payload
                                        // (i.e. next_hop_hmac == [0; 32])
                                        onion_payloads.pop();
@@ -638,7 +647,9 @@ fn do_blinded_intercept_payment(intercept_node_fails: bool) {
        expect_pending_htlcs_forwardable!(nodes[2]);
 
        expect_payment_claimable!(&nodes[2], payment_hash, payment_secret, amt_msat, None, nodes[2].node.get_our_node_id());
-       do_claim_payment_along_route(&nodes[0], &vec!(&vec!(&nodes[1], &nodes[2])[..]), false, payment_preimage);
+       do_claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage)
+       );
        expect_payment_sent(&nodes[0], payment_preimage, Some(Some(1000)), true, true);
 }
 
@@ -872,8 +883,9 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
                        let session_priv = SecretKey::from_slice(&session_priv).unwrap();
                        let mut onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
                        let cur_height = nodes[0].best_block_info().1;
+                       let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
                        let (mut onion_payloads, ..) = onion_utils::build_onion_payloads(
-                               &route.paths[0], amt_msat, RecipientOnionFields::spontaneous_empty(), cur_height, &None).unwrap();
+                               &route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None).unwrap();
 
                        let update_add = &mut payment_event_1_2.msgs[0];
                        onion_payloads.last_mut().map(|p| {
@@ -1183,3 +1195,139 @@ fn conditionally_round_fwd_amt() {
        let expected_fee = pass_claimed_payment_along_route(args);
        expect_payment_sent(&nodes[0], payment_preimage, Some(Some(expected_fee)), true, true);
 }
+
+#[test]
+fn blinded_keysend() {
+       let mut mpp_keysend_config = test_default_channel_config();
+       mpp_keysend_config.accept_mpp_keysend = true;
+       let chanmon_cfgs = create_chanmon_cfgs(3);
+       let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, Some(mpp_keysend_config)]);
+       let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+       create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
+       let chan_upd_1_2 = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0).0.contents;
+
+       let amt_msat = 5000;
+       let (keysend_preimage, _, payment_secret) = get_payment_preimage_hash(&nodes[2], None, None);
+       let route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1,
+               1_0000_0000,
+               nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(),
+               &[&chan_upd_1_2], &chanmon_cfgs[2].keys_manager);
+
+       let payment_hash = nodes[0].node.send_spontaneous_payment_with_retry(Some(keysend_preimage), RecipientOnionFields::spontaneous_empty(), PaymentId(keysend_preimage.0), route_params, Retry::Attempts(0)).unwrap();
+       check_added_monitors(&nodes[0], 1);
+
+       let expected_route: &[&[&Node]] = &[&[&nodes[1], &nodes[2]]];
+       let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+       assert_eq!(events.len(), 1);
+
+       let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
+       pass_along_path(&nodes[0], expected_route[0], amt_msat, payment_hash, Some(payment_secret), ev.clone(), true, Some(keysend_preimage));
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], expected_route, keysend_preimage)
+       );
+}
+
+#[test]
+fn blinded_mpp_keysend() {
+       let mut mpp_keysend_config = test_default_channel_config();
+       mpp_keysend_config.accept_mpp_keysend = true;
+       let chanmon_cfgs = create_chanmon_cfgs(4);
+       let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, Some(mpp_keysend_config)]);
+       let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
+
+       create_announced_chan_between_nodes(&nodes, 0, 1);
+       create_announced_chan_between_nodes(&nodes, 0, 2);
+       let chan_1_3 = create_announced_chan_between_nodes(&nodes, 1, 3);
+       let chan_2_3 = create_announced_chan_between_nodes(&nodes, 2, 3);
+
+       let amt_msat = 15_000_000;
+       let (keysend_preimage, _, payment_secret) = get_payment_preimage_hash(&nodes[3], None, None);
+       let route_params = {
+               let pay_params = PaymentParameters::blinded(
+                       vec![
+                               blinded_payment_path(payment_secret, 1, 1_0000_0000,
+                                       vec![nodes[1].node.get_our_node_id(), nodes[3].node.get_our_node_id()], &[&chan_1_3.0.contents],
+                                       &chanmon_cfgs[3].keys_manager
+                               ),
+                               blinded_payment_path(payment_secret, 1, 1_0000_0000,
+                                       vec![nodes[2].node.get_our_node_id(), nodes[3].node.get_our_node_id()], &[&chan_2_3.0.contents],
+                                       &chanmon_cfgs[3].keys_manager
+                               ),
+                       ]
+               )
+                       .with_bolt12_features(channelmanager::provided_bolt12_invoice_features(&UserConfig::default()))
+                       .unwrap();
+               RouteParameters::from_payment_params_and_value(pay_params, amt_msat)
+       };
+
+       let payment_hash = nodes[0].node.send_spontaneous_payment_with_retry(Some(keysend_preimage), RecipientOnionFields::spontaneous_empty(), PaymentId(keysend_preimage.0), route_params, Retry::Attempts(0)).unwrap();
+       check_added_monitors!(nodes[0], 2);
+
+       let expected_route: &[&[&Node]] = &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]];
+       let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+       assert_eq!(events.len(), 2);
+
+       let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
+       pass_along_path(&nodes[0], expected_route[0], amt_msat, payment_hash.clone(),
+               Some(payment_secret), ev.clone(), false, Some(keysend_preimage));
+
+       let ev = remove_first_msg_event_to_node(&nodes[2].node.get_our_node_id(), &mut events);
+       pass_along_path(&nodes[0], expected_route[1], amt_msat, payment_hash.clone(),
+               Some(payment_secret), ev.clone(), true, Some(keysend_preimage));
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], expected_route, keysend_preimage)
+       );
+}
+
+#[test]
+fn custom_tlvs_to_blinded_path() {
+       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_upd = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0).0.contents;
+
+       let amt_msat = 5000;
+       let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(amt_msat), None);
+       let payee_tlvs = ReceiveTlvs {
+               payment_secret,
+               payment_constraints: PaymentConstraints {
+                       max_cltv_expiry: u32::max_value(),
+                       htlc_minimum_msat: chan_upd.htlc_minimum_msat,
+               },
+               payment_context: PaymentContext::unknown(),
+       };
+       let mut secp_ctx = Secp256k1::new();
+       let blinded_path = BlindedPath::one_hop_for_payment(
+               nodes[1].node.get_our_node_id(), payee_tlvs, TEST_FINAL_CLTV as u16,
+               &chanmon_cfgs[1].keys_manager, &secp_ctx
+       ).unwrap();
+
+       let route_params = RouteParameters::from_payment_params_and_value(
+               PaymentParameters::blinded(vec![blinded_path]),
+               amt_msat,
+       );
+
+       let recipient_onion_fields = RecipientOnionFields::spontaneous_empty()
+               .with_custom_tlvs(vec![((1 << 16) + 1, vec![42, 42])])
+               .unwrap();
+       nodes[0].node.send_payment(payment_hash, recipient_onion_fields.clone(),
+               PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
+       check_added_monitors(&nodes[0], 1);
+
+       let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+       assert_eq!(events.len(), 1);
+       let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
+
+       let path = &[&nodes[1]];
+       let args = PassAlongPathArgs::new(&nodes[0], path, amt_msat, payment_hash, ev)
+               .with_payment_secret(payment_secret)
+               .with_custom_tlvs(recipient_onion_fields.custom_tlvs.clone());
+       do_pass_along_path(args);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1]]], payment_preimage)
+                       .with_custom_tlvs(recipient_onion_fields.custom_tlvs.clone())
+       );
+}
index 18c4d83406c1bdbcdb673b0461062de62f400b0a..b7d3aec39dc125bc2927c780bdb05aee68b29d5b 100644 (file)
 //! Various utilities for building scripts related to channels. These are
 //! largely of interest for those implementing the traits on [`crate::sign`] by hand.
 
+use bitcoin::{PubkeyHash, WPubkeyHash};
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::script::{Script, ScriptBuf, Builder};
 use bitcoin::blockdata::opcodes;
 use bitcoin::blockdata::transaction::{TxIn,TxOut,OutPoint,Transaction};
 use bitcoin::sighash;
 use bitcoin::sighash::EcdsaSighashType;
-use bitcoin::address::Payload;
+use bitcoin::transaction::Version;
 
 use bitcoin::hashes::{Hash, HashEngine};
+use bitcoin::hashes::hash160::Hash as Hash160;
 use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::hashes::ripemd160::Hash as Ripemd160;
-use bitcoin::hash_types::{Txid, PubkeyHash, WPubkeyHash};
+use bitcoin::hash_types::Txid;
 
 use crate::chain::chaininterface::fee_for_weight;
 use crate::chain::package::WEIGHT_REVOKED_OUTPUT;
 use crate::sign::EntropySource;
-use crate::ln::{PaymentHash, PaymentPreimage};
+use crate::ln::types::{PaymentHash, PaymentPreimage};
 use crate::ln::msgs::DecodeError;
 use crate::util::ser::{Readable, RequiredWrapper, Writeable, Writer};
 use crate::util::transaction_utils;
 
 use bitcoin::blockdata::locktime::absolute::LockTime;
+use bitcoin::ecdsa::Signature as BitcoinSignature;
 use bitcoin::secp256k1::{SecretKey, PublicKey, Scalar};
 use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Message};
 use bitcoin::{secp256k1, Sequence, Witness};
-use bitcoin::PublicKey as BitcoinPublicKey;
 
 use crate::io;
-use crate::prelude::*;
 use core::cmp;
 use crate::ln::chan_utils;
 use crate::util::transaction_utils::sort_outputs;
@@ -48,6 +50,9 @@ use crate::ln::features::ChannelTypeFeatures;
 use crate::crypto::utils::{sign, sign_with_aux_rand};
 use super::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcKey, HtlcBasepoint, RevocationKey, RevocationBasepoint};
 
+#[allow(unused_imports)]
+use crate::prelude::*;
+
 /// Maximum number of one-way in-flight HTLC (protocol-level value).
 pub const MAX_HTLCS: u16 = 483;
 /// The weight of a BIP141 witnessScript for a BOLT3's "offered HTLC output" on a commitment transaction, non-anchor variant.
@@ -184,7 +189,7 @@ pub fn build_commitment_secret(commitment_seed: &[u8; 32], idx: u64) -> [u8; 32]
 }
 
 /// Build a closing transaction
-pub fn build_closing_transaction(to_holder_value_sat: u64, to_counterparty_value_sat: u64, to_holder_script: ScriptBuf, to_counterparty_script: ScriptBuf, funding_outpoint: OutPoint) -> Transaction {
+pub fn build_closing_transaction(to_holder_value_sat: Amount, to_counterparty_value_sat: Amount, to_holder_script: ScriptBuf, to_counterparty_script: ScriptBuf, funding_outpoint: OutPoint) -> Transaction {
        let txins = {
                let mut ins: Vec<TxIn> = Vec::new();
                ins.push(TxIn {
@@ -198,14 +203,14 @@ pub fn build_closing_transaction(to_holder_value_sat: u64, to_counterparty_value
 
        let mut txouts: Vec<(TxOut, ())> = Vec::new();
 
-       if to_counterparty_value_sat > 0 {
+       if to_counterparty_value_sat > Amount::ZERO {
                txouts.push((TxOut {
                        script_pubkey: to_counterparty_script,
                        value: to_counterparty_value_sat
                }, ()));
        }
 
-       if to_holder_value_sat > 0 {
+       if to_holder_value_sat > Amount::ZERO {
                txouts.push((TxOut {
                        script_pubkey: to_holder_script,
                        value: to_holder_value_sat
@@ -220,7 +225,7 @@ pub fn build_closing_transaction(to_holder_value_sat: u64, to_counterparty_value
        }
 
        Transaction {
-               version: 2,
+               version: Version::TWO,
                lock_time: LockTime::ZERO,
                input: txins,
                output: outputs,
@@ -513,9 +518,9 @@ pub fn get_revokeable_redeemscript(revocation_key: &RevocationKey, contest_delay
 /// the channel type.
 pub fn get_counterparty_payment_script(channel_type_features: &ChannelTypeFeatures, payment_key: &PublicKey) -> ScriptBuf {
        if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
-               get_to_countersignatory_with_anchors_redeemscript(payment_key).to_v0_p2wsh()
+               get_to_countersignatory_with_anchors_redeemscript(payment_key).to_p2wsh()
        } else {
-               ScriptBuf::new_v0_p2wpkh(&WPubkeyHash::hash(&payment_key.serialize()))
+               ScriptBuf::new_p2wpkh(&WPubkeyHash::hash(&payment_key.serialize()))
        }
 }
 
@@ -540,6 +545,15 @@ pub struct HTLCOutputInCommitment {
        pub transaction_output_index: Option<u32>,
 }
 
+impl HTLCOutputInCommitment {
+       /// Converts HTLC's value with millisatoshi precision into [bitcoin::Amount] with satoshi precision.
+       /// Typically this conversion is needed when transitioning from LN into base-layer Bitcoin,
+       /// e. g. in commitment transactions.
+       pub const fn to_bitcoin_amount(&self) -> Amount {
+               Amount::from_sat(self.amount_msat / 1000)
+       }
+}
+
 impl_writeable_tlv_based!(HTLCOutputInCommitment, {
        (0, offered, required),
        (2, amount_msat, required),
@@ -668,7 +682,7 @@ pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, conte
        ));
 
        Transaction {
-               version: 2,
+               version: Version::TWO,
                lock_time: LockTime::from_consensus(if htlc.offered { htlc.cltv_expiry } else { 0 }),
                input: txins,
                output: txouts,
@@ -696,14 +710,14 @@ pub(crate) fn build_htlc_output(
                htlc_success_tx_weight(channel_type_features)
        };
        let output_value = if channel_type_features.supports_anchors_zero_fee_htlc_tx() && !channel_type_features.supports_anchors_nonzero_fee_htlc_tx() {
-               htlc.amount_msat / 1000
+               htlc.to_bitcoin_amount()
        } else {
-               let total_fee = feerate_per_kw as u64 * weight / 1000;
-               htlc.amount_msat / 1000 - total_fee
+               let total_fee = Amount::from_sat(feerate_per_kw as u64 * weight / 1000);
+               htlc.to_bitcoin_amount() - total_fee
        };
 
        TxOut {
-               script_pubkey: get_revokeable_redeemscript(revocation_key, contest_delay, broadcaster_delayed_payment_key).to_v0_p2wsh(),
+               script_pubkey: get_revokeable_redeemscript(revocation_key, contest_delay, broadcaster_delayed_payment_key).to_p2wsh(),
                value: output_value,
        }
 }
@@ -722,8 +736,8 @@ pub fn build_htlc_input_witness(
        let mut witness = Witness::new();
        // First push the multisig dummy, note that due to BIP147 (NULLDUMMY) it must be a zero-length element.
        witness.push(vec![]);
-       witness.push_bitcoin_signature(&remote_sig.serialize_der(), remote_sighash_type);
-       witness.push_bitcoin_signature(&local_sig.serialize_der(), EcdsaSighashType::All);
+       witness.push_ecdsa_signature(&BitcoinSignature { sig: *remote_sig, hash_ty: remote_sighash_type });
+       witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(*local_sig));
        if let Some(preimage) = preimage {
                witness.push(preimage.0.to_vec());
        } else {
@@ -796,7 +810,7 @@ pub fn get_anchor_redeemscript(funding_pubkey: &PublicKey) -> ScriptBuf {
 
 /// Locates the output with an anchor script paying to `funding_pubkey` within `commitment_tx`.
 pub(crate) fn get_anchor_output<'a>(commitment_tx: &'a Transaction, funding_pubkey: &PublicKey) -> Option<(u32, &'a TxOut)> {
-       let anchor_script = chan_utils::get_anchor_redeemscript(funding_pubkey).to_v0_p2wsh();
+       let anchor_script = chan_utils::get_anchor_redeemscript(funding_pubkey).to_p2wsh();
        commitment_tx.output.iter().enumerate()
                .find(|(_, txout)| txout.script_pubkey == anchor_script)
                .map(|(idx, txout)| (idx as u32, txout))
@@ -806,7 +820,7 @@ pub(crate) fn get_anchor_output<'a>(commitment_tx: &'a Transaction, funding_pubk
 pub fn build_anchor_input_witness(funding_key: &PublicKey, funding_sig: &Signature) -> Witness {
        let anchor_redeem_script = chan_utils::get_anchor_redeemscript(funding_key);
        let mut ret = Witness::new();
-       ret.push_bitcoin_signature(&funding_sig.serialize_der(), EcdsaSighashType::All);
+       ret.push_ecdsa_signature(&BitcoinSignature::sighash_all(*funding_sig));
        ret.push(anchor_redeem_script.as_bytes());
        ret
 }
@@ -850,6 +864,11 @@ impl ChannelTransactionParameters {
                self.counterparty_parameters.is_some() && self.funding_outpoint.is_some()
        }
 
+       /// Whether the channel supports zero-fee HTLC transaction anchors.
+       pub(crate) fn supports_anchors(&self) -> bool {
+               self.channel_type_features.supports_anchors_zero_fee_htlc_tx()
+       }
+
        /// Convert the holder/counterparty parameters to broadcaster/countersignatory-organized parameters,
        /// given that the holder is the broadcaster.
        ///
@@ -1029,7 +1048,7 @@ impl HolderCommitmentTransaction {
        pub fn dummy(htlcs: &mut Vec<(HTLCOutputInCommitment, ())>) -> Self {
                let secp_ctx = Secp256k1::new();
                let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
-               let dummy_sig = sign(&secp_ctx, &secp256k1::Message::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[42; 32]).unwrap());
+               let dummy_sig = sign(&secp_ctx, &secp256k1::Message::from_digest([42; 32]), &SecretKey::from_slice(&[42; 32]).unwrap());
 
                let keys = TxCreationKeys {
                        per_commitment_point: dummy_key.clone(),
@@ -1084,11 +1103,11 @@ impl HolderCommitmentTransaction {
                tx.input[0].witness.push(Vec::new());
 
                if self.holder_sig_first {
-                       tx.input[0].witness.push_bitcoin_signature(&holder_sig.serialize_der(), EcdsaSighashType::All);
-                       tx.input[0].witness.push_bitcoin_signature(&self.counterparty_sig.serialize_der(), EcdsaSighashType::All);
+                       tx.input[0].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(holder_sig));
+                       tx.input[0].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(self.counterparty_sig));
                } else {
-                       tx.input[0].witness.push_bitcoin_signature(&self.counterparty_sig.serialize_der(), EcdsaSighashType::All);
-                       tx.input[0].witness.push_bitcoin_signature(&holder_sig.serialize_der(), EcdsaSighashType::All);
+                       tx.input[0].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(self.counterparty_sig));
+                       tx.input[0].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(holder_sig));
                }
 
                tx.input[0].witness.push(funding_redeemscript.as_bytes().to_vec());
@@ -1118,7 +1137,7 @@ impl BuiltCommitmentTransaction {
        ///
        /// This can be used to verify a signature.
        pub fn get_sighash_all(&self, funding_redeemscript: &Script, channel_value_satoshis: u64) -> Message {
-               let sighash = &sighash::SighashCache::new(&self.transaction).segwit_signature_hash(0, funding_redeemscript, channel_value_satoshis, EcdsaSighashType::All).unwrap()[..];
+               let sighash = &sighash::SighashCache::new(&self.transaction).p2wsh_signature_hash(0, funding_redeemscript, Amount::from_sat(channel_value_satoshis), EcdsaSighashType::All).unwrap()[..];
                hash_to_message!(sighash)
        }
 
@@ -1145,8 +1164,8 @@ impl BuiltCommitmentTransaction {
 /// secret key.
 #[derive(Clone, Hash, PartialEq, Eq)]
 pub struct ClosingTransaction {
-       to_holder_value_sat: u64,
-       to_counterparty_value_sat: u64,
+       to_holder_value_sat: Amount,
+       to_counterparty_value_sat: Amount,
        to_holder_script: ScriptBuf,
        to_counterparty_script: ScriptBuf,
        built: Transaction,
@@ -1161,6 +1180,8 @@ impl ClosingTransaction {
                to_counterparty_script: ScriptBuf,
                funding_outpoint: OutPoint,
        ) -> Self {
+               let to_holder_value_sat = Amount::from_sat(to_holder_value_sat);
+               let to_counterparty_value_sat = Amount::from_sat(to_counterparty_value_sat);
                let built = build_closing_transaction(
                        to_holder_value_sat, to_counterparty_value_sat,
                        to_holder_script.clone(), to_counterparty_script.clone(),
@@ -1205,12 +1226,12 @@ impl ClosingTransaction {
 
        /// The value to be sent to the holder, or zero if the output will be omitted
        pub fn to_holder_value_sat(&self) -> u64 {
-               self.to_holder_value_sat
+               self.to_holder_value_sat.to_sat()
        }
 
        /// The value to be sent to the counterparty, or zero if the output will be omitted
        pub fn to_counterparty_value_sat(&self) -> u64 {
-               self.to_counterparty_value_sat
+               self.to_counterparty_value_sat.to_sat()
        }
 
        /// The destination of the holder's output
@@ -1250,7 +1271,7 @@ impl<'a> TrustedClosingTransaction<'a> {
        ///
        /// This can be used to verify a signature.
        pub fn get_sighash_all(&self, funding_redeemscript: &Script, channel_value_satoshis: u64) -> Message {
-               let sighash = &sighash::SighashCache::new(&self.inner.built).segwit_signature_hash(0, funding_redeemscript, channel_value_satoshis, EcdsaSighashType::All).unwrap()[..];
+               let sighash = &sighash::SighashCache::new(&self.inner.built).p2wsh_signature_hash(0, funding_redeemscript, Amount::from_sat(channel_value_satoshis), EcdsaSighashType::All).unwrap()[..];
                hash_to_message!(sighash)
        }
 
@@ -1271,8 +1292,8 @@ impl<'a> TrustedClosingTransaction<'a> {
 #[derive(Clone, Debug)]
 pub struct CommitmentTransaction {
        commitment_number: u64,
-       to_broadcaster_value_sat: u64,
-       to_countersignatory_value_sat: u64,
+       to_broadcaster_value_sat: Amount,
+       to_countersignatory_value_sat: Amount,
        to_broadcaster_delay: Option<u16>, // Added in 0.0.117
        feerate_per_kw: u32,
        htlcs: Vec<HTLCOutputInCommitment>,
@@ -1366,6 +1387,9 @@ impl CommitmentTransaction {
        ///
        /// This is not exported to bindings users due to the generic though we likely should expose a version without
        pub fn new_with_auxiliary_htlc_data<T>(commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, broadcaster_funding_key: PublicKey, countersignatory_funding_key: PublicKey, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> CommitmentTransaction {
+               let to_broadcaster_value_sat = Amount::from_sat(to_broadcaster_value_sat);
+               let to_countersignatory_value_sat = Amount::from_sat(to_countersignatory_value_sat);
+
                // Sort outputs and populate output indices while keeping track of the auxiliary data
                let (outputs, htlcs) = Self::internal_build_outputs(&keys, to_broadcaster_value_sat, to_countersignatory_value_sat, htlcs_with_aux, channel_parameters, &broadcaster_funding_key, &countersignatory_funding_key).unwrap();
 
@@ -1413,7 +1437,7 @@ impl CommitmentTransaction {
 
        fn make_transaction(obscured_commitment_transaction_number: u64, txins: Vec<TxIn>, outputs: Vec<TxOut>) -> Transaction {
                Transaction {
-                       version: 2,
+                       version: Version::TWO,
                        lock_time: LockTime::from_consensus(((0x20 as u32) << 8 * 3) | ((obscured_commitment_transaction_number & 0xffffffu64) as u32)),
                        input: txins,
                        output: outputs,
@@ -1424,17 +1448,17 @@ impl CommitmentTransaction {
        // - initial sorting of outputs / HTLCs in the constructor, in which case T is auxiliary data the
        //   caller needs to have sorted together with the HTLCs so it can keep track of the output index
        // - building of a bitcoin transaction during a verify() call, in which case T is just ()
-       fn internal_build_outputs<T>(keys: &TxCreationKeys, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<(Vec<TxOut>, Vec<HTLCOutputInCommitment>), ()> {
+       fn internal_build_outputs<T>(keys: &TxCreationKeys, to_broadcaster_value_sat: Amount, to_countersignatory_value_sat: Amount, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<(Vec<TxOut>, Vec<HTLCOutputInCommitment>), ()> {
                let countersignatory_pubkeys = channel_parameters.countersignatory_pubkeys();
                let contest_delay = channel_parameters.contest_delay();
 
                let mut txouts: Vec<(TxOut, Option<&mut HTLCOutputInCommitment>)> = Vec::new();
 
-               if to_countersignatory_value_sat > 0 {
+               if to_countersignatory_value_sat > Amount::ZERO {
                        let script = if channel_parameters.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
-                           get_to_countersignatory_with_anchors_redeemscript(&countersignatory_pubkeys.payment_point).to_v0_p2wsh()
+                               get_to_countersignatory_with_anchors_redeemscript(&countersignatory_pubkeys.payment_point).to_p2wsh()
                        } else {
-                           Payload::p2wpkh(&BitcoinPublicKey::new(countersignatory_pubkeys.payment_point)).unwrap().script_pubkey()
+                               ScriptBuf::new_p2wpkh(&Hash160::hash(&countersignatory_pubkeys.payment_point.serialize()).into())
                        };
                        txouts.push((
                                TxOut {
@@ -1445,7 +1469,7 @@ impl CommitmentTransaction {
                        ))
                }
 
-               if to_broadcaster_value_sat > 0 {
+               if to_broadcaster_value_sat > Amount::ZERO {
                        let redeem_script = get_revokeable_redeemscript(
                                &keys.revocation_key,
                                contest_delay,
@@ -1453,7 +1477,7 @@ impl CommitmentTransaction {
                        );
                        txouts.push((
                                TxOut {
-                                       script_pubkey: redeem_script.to_v0_p2wsh(),
+                                       script_pubkey: redeem_script.to_p2wsh(),
                                        value: to_broadcaster_value_sat,
                                },
                                None,
@@ -1461,23 +1485,23 @@ impl CommitmentTransaction {
                }
 
                if channel_parameters.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
-                       if to_broadcaster_value_sat > 0 || !htlcs_with_aux.is_empty() {
+                       if to_broadcaster_value_sat > Amount::ZERO || !htlcs_with_aux.is_empty() {
                                let anchor_script = get_anchor_redeemscript(broadcaster_funding_key);
                                txouts.push((
                                        TxOut {
-                                               script_pubkey: anchor_script.to_v0_p2wsh(),
-                                               value: ANCHOR_OUTPUT_VALUE_SATOSHI,
+                                               script_pubkey: anchor_script.to_p2wsh(),
+                                               value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI),
                                        },
                                        None,
                                ));
                        }
 
-                       if to_countersignatory_value_sat > 0 || !htlcs_with_aux.is_empty() {
+                       if to_countersignatory_value_sat > Amount::ZERO || !htlcs_with_aux.is_empty() {
                                let anchor_script = get_anchor_redeemscript(countersignatory_funding_key);
                                txouts.push((
                                        TxOut {
-                                               script_pubkey: anchor_script.to_v0_p2wsh(),
-                                               value: ANCHOR_OUTPUT_VALUE_SATOSHI,
+                                               script_pubkey: anchor_script.to_p2wsh(),
+                                               value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI),
                                        },
                                        None,
                                ));
@@ -1488,8 +1512,8 @@ impl CommitmentTransaction {
                for (htlc, _) in htlcs_with_aux {
                        let script = chan_utils::get_htlc_redeemscript(&htlc, &channel_parameters.channel_type_features(), &keys);
                        let txout = TxOut {
-                               script_pubkey: script.to_v0_p2wsh(),
-                               value: htlc.amount_msat / 1000,
+                               script_pubkey: script.to_p2wsh(),
+                               value: htlc.to_bitcoin_amount(),
                        };
                        txouts.push((txout, Some(htlc)));
                }
@@ -1559,12 +1583,12 @@ impl CommitmentTransaction {
 
        /// The value to be sent to the broadcaster
        pub fn to_broadcaster_value_sat(&self) -> u64 {
-               self.to_broadcaster_value_sat
+               self.to_broadcaster_value_sat.to_sat()
        }
 
        /// The value to be sent to the counterparty
        pub fn to_countersignatory_value_sat(&self) -> u64 {
-               self.to_countersignatory_value_sat
+               self.to_countersignatory_value_sat.to_sat()
        }
 
        /// The feerate paid per 1000-weight-unit in this commitment transaction.
@@ -1672,7 +1696,7 @@ impl<'a> TrustedCommitmentTransaction<'a> {
 
                        let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, &self.channel_type_features, &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key);
 
-                       let sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, this_htlc.amount_msat / 1000, EcdsaSighashType::All).unwrap()[..]);
+                       let sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).p2wsh_signature_hash(0, &htlc_redeemscript, this_htlc.to_bitcoin_amount(), EcdsaSighashType::All).unwrap()[..]);
                        ret.push(sign_with_aux_rand(secp_ctx, &sighash, &holder_htlc_key, entropy_source));
                }
                Ok(ret)
@@ -1729,7 +1753,7 @@ impl<'a> TrustedCommitmentTransaction<'a> {
                        self.to_broadcaster_delay?,
                        &self.keys.broadcaster_delayed_payment_key,
                );
-               let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
+               let revokeable_p2wsh = revokeable_redeemscript.to_p2wsh();
                let outputs = &self.inner.built.transaction.output;
                outputs.iter().enumerate()
                        .find(|(_, out)| out.script_pubkey == revokeable_p2wsh)
@@ -1765,13 +1789,13 @@ impl<'a> TrustedCommitmentTransaction<'a> {
                        value,
                }];
                let mut justice_tx = Transaction {
-                       version: 2,
+                       version: Version::TWO,
                        lock_time: LockTime::ZERO,
                        input,
                        output,
                };
                let weight = justice_tx.weight().to_wu() + WEIGHT_REVOKED_OUTPUT;
-               let fee = fee_for_weight(feerate_per_kw as u32, weight);
+               let fee = Amount::from_sat(fee_for_weight(feerate_per_kw as u32, weight));
                justice_tx.output[0].value = value.checked_sub(fee).ok_or(())?;
                Ok(justice_tx)
        }
@@ -1812,7 +1836,6 @@ pub fn get_commitment_transaction_number_obscure_factor(
 mod tests {
        use super::{CounterpartyCommitmentSecrets, ChannelPublicKeys};
        use crate::chain;
-       use crate::prelude::*;
        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;
@@ -1820,11 +1843,14 @@ mod tests {
        use bitcoin::{Network, Txid, ScriptBuf};
        use bitcoin::hashes::Hash;
        use bitcoin::hashes::hex::FromHex;
-       use crate::ln::PaymentHash;
+       use crate::ln::types::PaymentHash;
        use bitcoin::address::Payload;
        use bitcoin::PublicKey as BitcoinPublicKey;
        use crate::ln::features::ChannelTypeFeatures;
 
+       #[allow(unused_imports)]
+       use crate::prelude::*;
+
        struct TestCommitmentTxBuilder {
                commitment_number: u64,
                holder_funding_pubkey: PublicKey,
@@ -1897,7 +1923,7 @@ mod tests {
                builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
                let tx = builder.build(1000, 2000);
                assert_eq!(tx.built.transaction.output.len(), 4);
-               assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&builder.counterparty_pubkeys.payment_point).to_v0_p2wsh());
+               assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&builder.counterparty_pubkeys.payment_point).to_p2wsh());
 
                // Generate broadcaster output and anchor
                let tx = builder.build(3000, 0);
@@ -1929,11 +1955,11 @@ mod tests {
                let tx = builder.build(3000, 0);
                let keys = &builder.keys.clone();
                assert_eq!(tx.built.transaction.output.len(), 3);
-               assert_eq!(tx.built.transaction.output[0].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh());
-               assert_eq!(tx.built.transaction.output[1].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh());
-               assert_eq!(get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh().to_hex_string(),
+               assert_eq!(tx.built.transaction.output[0].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_p2wsh());
+               assert_eq!(tx.built.transaction.output[1].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_p2wsh());
+               assert_eq!(get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_p2wsh().to_hex_string(),
                                   "0020e43a7c068553003fe68fcae424fb7b28ec5ce48cd8b6744b3945631389bad2fb");
-               assert_eq!(get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh().to_hex_string(),
+               assert_eq!(get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_p2wsh().to_hex_string(),
                                   "0020215d61bba56b19e9eadb6107f5a85d7f99c40f65992443f69229c290165bc00d");
 
                // Generate broadcaster output and received and offered HTLC outputs,  with anchors
@@ -1941,11 +1967,11 @@ mod tests {
                builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
                let tx = builder.build(3000, 0);
                assert_eq!(tx.built.transaction.output.len(), 5);
-               assert_eq!(tx.built.transaction.output[2].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh());
-               assert_eq!(tx.built.transaction.output[3].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh());
-               assert_eq!(get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh().to_hex_string(),
+               assert_eq!(tx.built.transaction.output[2].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_p2wsh());
+               assert_eq!(tx.built.transaction.output[3].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_p2wsh());
+               assert_eq!(get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_p2wsh().to_hex_string(),
                                   "0020b70d0649c72b38756885c7a30908d912a7898dd5d79457a7280b8e9a20f3f2bc");
-               assert_eq!(get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh().to_hex_string(),
+               assert_eq!(get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_p2wsh().to_hex_string(),
                                   "002087a3faeb1950a469c0e2db4a79b093a41b9526e5a6fc6ef5cb949bde3be379c7");
        }
 
@@ -1991,7 +2017,7 @@ mod tests {
                        .unwrap()[..]).unwrap();
                let pubkey_hash = BitcoinPublicKey::new(
                        PublicKey::from_secret_key(&Secp256k1::new(), &secret_key)).wpubkey_hash().unwrap();
-               let destination_script = ScriptBuf::new_v0_p2wpkh(&pubkey_hash);
+               let destination_script = ScriptBuf::new_p2wpkh(&pubkey_hash);
 
                let justice_tx = tx.trust().build_to_local_justice_tx(253, destination_script.clone()).unwrap();
                assert_eq!(justice_tx.input.len(), 1);
@@ -2000,7 +2026,7 @@ mod tests {
                assert!(justice_tx.input[0].sequence.is_rbf());
 
                assert_eq!(justice_tx.output.len(), 1);
-               assert!(justice_tx.output[0].value < 1000);
+               assert!(justice_tx.output[0].value.to_sat() < 1000);
                assert_eq!(justice_tx.output[0].script_pubkey, destination_script);
        }
 
index 2e95f5c63ff7e173bb5a107f0719fb89ca3e3301..54f376874843876370358006dcddc47e023b3aa9 100644 (file)
 
 use bitcoin::blockdata::constants::genesis_block;
 use bitcoin::hash_types::BlockHash;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 use crate::chain::channelmonitor::{ANTI_REORG_DELAY, ChannelMonitor};
 use crate::chain::transaction::OutPoint;
 use crate::chain::{ChannelMonitorUpdateStatus, Listen, Watch};
 use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose, ClosureReason, HTLCDestination};
 use crate::ln::channelmanager::{RAACommitmentOrder, PaymentSendFailure, PaymentId, RecipientOnionFields};
 use crate::ln::channel::{AnnouncementSigsState, ChannelPhase};
-use crate::ln::{msgs, ChannelId};
+use crate::ln::msgs;
+use crate::ln::types::ChannelId;
 use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler};
 use crate::util::test_channel_signer::TestChannelSigner;
 use crate::util::errors::APIError;
@@ -173,11 +174,11 @@ fn do_test_simple_monitor_temporary_update_fail(disconnect: bool) {
                        assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
                        assert_eq!(via_channel_id, Some(channel_id));
                        match &purpose {
-                               PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
+                               PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(payment_secret_1, *payment_secret);
                                },
-                               _ => panic!("expected PaymentPurpose::InvoicePayment")
+                               _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@ -554,11 +555,11 @@ fn do_test_monitor_temporary_update_fail(disconnect_count: usize) {
                        assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
                        assert_eq!(via_channel_id, Some(channel_id));
                        match &purpose {
-                               PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
+                               PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(payment_secret_2, *payment_secret);
                                },
-                               _ => panic!("expected PaymentPurpose::InvoicePayment")
+                               _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@ -672,11 +673,11 @@ fn test_monitor_update_fail_cs() {
                        assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
                        assert_eq!(via_channel_id, Some(channel_id));
                        match &purpose {
-                               PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
+                               PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(our_payment_secret, *payment_secret);
                                },
-                               _ => panic!("expected PaymentPurpose::InvoicePayment")
+                               _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@ -1683,11 +1684,11 @@ fn test_monitor_update_fail_claim() {
                        assert_eq!(via_channel_id, Some(channel_id));
                        assert_eq!(via_user_channel_id, Some(42));
                        match &purpose {
-                               PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
+                               PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(payment_secret_2, *payment_secret);
                                },
-                               _ => panic!("expected PaymentPurpose::InvoicePayment")
+                               _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@ -1699,11 +1700,11 @@ fn test_monitor_update_fail_claim() {
                        assert_eq!(receiver_node_id.unwrap(), nodes[0].node.get_our_node_id());
                        assert_eq!(via_channel_id, Some(channel_id));
                        match &purpose {
-                               PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
+                               PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(payment_secret_3, *payment_secret);
                                },
-                               _ => panic!("expected PaymentPurpose::InvoicePayment")
+                               _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@ -2024,7 +2025,9 @@ fn test_path_paused_mpp() {
        assert_eq!(events.len(), 1);
        pass_along_path(&nodes[0], &[&nodes[2], &nodes[3]], 200_000, payment_hash.clone(), Some(payment_secret), events.pop().unwrap(), true, None);
 
-       claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], payment_preimage)
+       );
 }
 
 #[test]
index 33df5303814da439f5f83fd33a2acf53963b115f..b8998cb45352277b095a7b9e2a0b7a898b0dc6a7 100644 (file)
@@ -7,6 +7,7 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::constants::ChainHash;
 use bitcoin::blockdata::script::{Script, ScriptBuf, Builder};
 use bitcoin::blockdata::transaction::Transaction;
@@ -24,7 +25,7 @@ use bitcoin::secp256k1::{PublicKey,SecretKey};
 use bitcoin::secp256k1::{Secp256k1,ecdsa::Signature};
 use bitcoin::secp256k1;
 
-use crate::ln::{ChannelId, PaymentPreimage, PaymentHash};
+use crate::ln::types::{ChannelId, PaymentPreimage, PaymentHash};
 use crate::ln::features::{ChannelTypeFeatures, InitFeatures};
 use crate::ln::msgs;
 use crate::ln::msgs::DecodeError;
@@ -37,7 +38,7 @@ use crate::chain::BestBlock;
 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::sign::ecdsa::{EcdsaChannelSigner, WriteableEcdsaChannelSigner};
+use crate::sign::ecdsa::EcdsaChannelSigner;
 use crate::sign::{EntropySource, ChannelSigner, SignerProvider, NodeSigner, Recipient};
 use crate::events::ClosureReason;
 use crate::routing::gossip::NodeId;
@@ -50,7 +51,6 @@ use crate::util::scid_utils::scid_from_parts;
 use crate::io;
 use crate::prelude::*;
 use core::{cmp,mem,fmt};
-use core::convert::TryInto;
 use core::ops::Deref;
 #[cfg(any(test, fuzzing, debug_assertions))]
 use crate::sync::Mutex;
@@ -104,10 +104,38 @@ enum InboundHTLCRemovalReason {
        Fulfill(PaymentPreimage),
 }
 
+/// Represents the resolution status of an inbound HTLC.
+#[derive(Clone)]
+enum InboundHTLCResolution {
+       /// Resolved implies the action we must take with the inbound HTLC has already been determined,
+       /// i.e., we already know whether it must be failed back or forwarded.
+       //
+       // TODO: Once this variant is removed, we should also clean up
+       // [`MonitorRestoreUpdates::accepted_htlcs`] as the path will be unreachable.
+       Resolved {
+               pending_htlc_status: PendingHTLCStatus,
+       },
+       /// Pending implies we will attempt to resolve the inbound HTLC once it has been fully committed
+       /// to by both sides of the channel, i.e., once a `revoke_and_ack` has been processed by both
+       /// nodes for the state update in which it was proposed.
+       Pending {
+               update_add_htlc: msgs::UpdateAddHTLC,
+       },
+}
+
+impl_writeable_tlv_based_enum!(InboundHTLCResolution,
+       (0, Resolved) => {
+               (0, pending_htlc_status, required),
+       },
+       (2, Pending) => {
+               (0, update_add_htlc, required),
+       };
+);
+
 enum InboundHTLCState {
        /// Offered by remote, to be included in next local commitment tx. I.e., the remote sent an
        /// update_add_htlc message for this HTLC.
-       RemoteAnnounced(PendingHTLCStatus),
+       RemoteAnnounced(InboundHTLCResolution),
        /// Included in a received commitment_signed message (implying we've
        /// revoke_and_ack'd it), but the remote hasn't yet revoked their previous
        /// state (see the example below). We have not yet included this HTLC in a
@@ -137,13 +165,13 @@ enum InboundHTLCState {
        /// Implies AwaitingRemoteRevoke.
        ///
        /// [BOLT #2]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md
-       AwaitingRemoteRevokeToAnnounce(PendingHTLCStatus),
+       AwaitingRemoteRevokeToAnnounce(InboundHTLCResolution),
        /// Included in a received commitment_signed message (implying we've revoke_and_ack'd it).
        /// We have also included this HTLC in our latest commitment_signed and are now just waiting
        /// on the remote's revoke_and_ack to make this HTLC an irrevocable part of the state of the
        /// channel (before it can then get forwarded and/or removed).
        /// Implies AwaitingRemoteRevoke.
-       AwaitingAnnouncedRemoteRevoke(PendingHTLCStatus),
+       AwaitingAnnouncedRemoteRevoke(InboundHTLCResolution),
        Committed,
        /// Removed by us and a new commitment_signed was sent (if we were AwaitingRemoteRevoke when we
        /// created it we would have put it in the holding cell instead). When they next revoke_and_ack
@@ -896,25 +924,28 @@ pub(super) struct WithChannelContext<'a, L: Deref> where L::Target: Logger {
        pub logger: &'a L,
        pub peer_id: Option<PublicKey>,
        pub channel_id: Option<ChannelId>,
+       pub payment_hash: Option<PaymentHash>,
 }
 
 impl<'a, L: Deref> Logger for WithChannelContext<'a, L> where L::Target: Logger {
        fn log(&self, mut record: Record) {
                record.peer_id = self.peer_id;
                record.channel_id = self.channel_id;
+               record.payment_hash = self.payment_hash;
                self.logger.log(record)
        }
 }
 
 impl<'a, 'b, L: Deref> WithChannelContext<'a, L>
 where L::Target: Logger {
-       pub(super) fn from<S: Deref>(logger: &'a L, context: &'b ChannelContext<S>) -> Self
+       pub(super) fn from<S: Deref>(logger: &'a L, context: &'b ChannelContext<S>, payment_hash: Option<PaymentHash>) -> Self
        where S::Target: SignerProvider
        {
                WithChannelContext {
                        logger,
                        peer_id: Some(context.counterparty_node_id),
                        channel_id: Some(context.channel_id),
+                       payment_hash
                }
        }
 }
@@ -971,14 +1002,16 @@ enum HTLCInitiator {
        RemoteOffered,
 }
 
-/// An enum gathering stats on pending HTLCs, either inbound or outbound side.
+/// Current counts of various HTLCs, useful for calculating current balances available exactly.
 struct HTLCStats {
-       pending_htlcs: u32,
-       pending_htlcs_value_msat: u64,
+       pending_inbound_htlcs: usize,
+       pending_outbound_htlcs: usize,
+       pending_inbound_htlcs_value_msat: u64,
+       pending_outbound_htlcs_value_msat: u64,
        on_counterparty_tx_dust_exposure_msat: u64,
        on_holder_tx_dust_exposure_msat: u64,
-       holding_cell_msat: u64,
-       on_holder_tx_holding_cell_htlcs_count: u32, // dust HTLCs *non*-included
+       outbound_holding_cell_msat: u64,
+       on_holder_tx_outbound_holding_cell_htlcs_count: u32, // dust HTLCs *non*-included
 }
 
 /// An enum gathering stats on commitment transaction, either local or remote.
@@ -1044,6 +1077,7 @@ pub(super) struct MonitorRestoreUpdates {
        pub accepted_htlcs: Vec<(PendingHTLCInfo, u64)>,
        pub failed_htlcs: Vec<(HTLCSource, PaymentHash, HTLCFailReason)>,
        pub finalized_claimed_htlcs: Vec<HTLCSource>,
+       pub pending_update_adds: Vec<msgs::UpdateAddHTLC>,
        pub funding_broadcastable: Option<Transaction>,
        pub channel_ready: Option<msgs::ChannelReady>,
        pub announcement_sigs: Option<msgs::AnnouncementSignatures>,
@@ -1161,9 +1195,9 @@ impl_writeable_tlv_based!(PendingChannelMonitorUpdate, {
 pub(super) enum ChannelPhase<SP: Deref> where SP::Target: SignerProvider {
        UnfundedOutboundV1(OutboundV1Channel<SP>),
        UnfundedInboundV1(InboundV1Channel<SP>),
-       #[cfg(dual_funding)]
+       #[cfg(any(dual_funding, splicing))]
        UnfundedOutboundV2(OutboundV2Channel<SP>),
-       #[cfg(dual_funding)]
+       #[cfg(any(dual_funding, splicing))]
        UnfundedInboundV2(InboundV2Channel<SP>),
        Funded(Channel<SP>),
 }
@@ -1177,9 +1211,9 @@ impl<'a, SP: Deref> ChannelPhase<SP> where
                        ChannelPhase::Funded(chan) => &chan.context,
                        ChannelPhase::UnfundedOutboundV1(chan) => &chan.context,
                        ChannelPhase::UnfundedInboundV1(chan) => &chan.context,
-                       #[cfg(dual_funding)]
+                       #[cfg(any(dual_funding, splicing))]
                        ChannelPhase::UnfundedOutboundV2(chan) => &chan.context,
-                       #[cfg(dual_funding)]
+                       #[cfg(any(dual_funding, splicing))]
                        ChannelPhase::UnfundedInboundV2(chan) => &chan.context,
                }
        }
@@ -1189,9 +1223,9 @@ impl<'a, SP: Deref> ChannelPhase<SP> where
                        ChannelPhase::Funded(ref mut chan) => &mut chan.context,
                        ChannelPhase::UnfundedOutboundV1(ref mut chan) => &mut chan.context,
                        ChannelPhase::UnfundedInboundV1(ref mut chan) => &mut chan.context,
-                       #[cfg(dual_funding)]
+                       #[cfg(any(dual_funding, splicing))]
                        ChannelPhase::UnfundedOutboundV2(ref mut chan) => &mut chan.context,
-                       #[cfg(dual_funding)]
+                       #[cfg(any(dual_funding, splicing))]
                        ChannelPhase::UnfundedInboundV2(ref mut chan) => &mut chan.context,
                }
        }
@@ -1291,6 +1325,7 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
        monitor_pending_forwards: Vec<(PendingHTLCInfo, u64)>,
        monitor_pending_failures: Vec<(HTLCSource, PaymentHash, HTLCFailReason)>,
        monitor_pending_finalized_fulfills: Vec<HTLCSource>,
+       monitor_pending_update_adds: Vec<msgs::UpdateAddHTLC>,
 
        /// If we went to send a commitment update (ie some messages then [`msgs::CommitmentSigned`])
        /// but our signer (initially) refused to give us a signature, we should retry at some point in
@@ -1369,7 +1404,7 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
        /// Either the height at which this channel was created or the height at which it was last
        /// serialized if it was serialized by versions prior to 0.0.103.
        /// We use this to close if funding is never broadcasted.
-       channel_creation_height: u32,
+       pub(super) channel_creation_height: u32,
 
        counterparty_dust_limit_satoshis: u64,
 
@@ -1540,7 +1575,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                        L::Target: Logger,
                        SP::Target: SignerProvider,
        {
-               let logger = WithContext::from(logger, Some(counterparty_node_id), Some(open_channel_fields.temporary_channel_id));
+               let logger = WithContext::from(logger, Some(counterparty_node_id), Some(open_channel_fields.temporary_channel_id), None);
                let announced_channel = if (open_channel_fields.channel_flags & 1) == 1 { true } else { false };
 
                let channel_value_satoshis = our_funding_satoshis.saturating_add(open_channel_fields.funding_satoshis);
@@ -1755,6 +1790,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                        monitor_pending_forwards: Vec::new(),
                        monitor_pending_failures: Vec::new(),
                        monitor_pending_finalized_fulfills: Vec::new(),
+                       monitor_pending_update_adds: Vec::new(),
 
                        signer_pending_commitment_update: false,
                        signer_pending_funding: false,
@@ -1976,6 +2012,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                        monitor_pending_forwards: Vec::new(),
                        monitor_pending_failures: Vec::new(),
                        monitor_pending_finalized_fulfills: Vec::new(),
+                       monitor_pending_update_adds: Vec::new(),
 
                        signer_pending_commitment_update: false,
                        signer_pending_funding: false,
@@ -2306,15 +2343,16 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                cmp::max(self.config.options.cltv_expiry_delta, MIN_CLTV_EXPIRY_DELTA)
        }
 
-       pub fn get_max_dust_htlc_exposure_msat<F: Deref>(&self,
-               fee_estimator: &LowerBoundedFeeEstimator<F>) -> u64
-       where F::Target: FeeEstimator
-       {
+       fn get_dust_exposure_limiting_feerate<F: Deref>(&self,
+               fee_estimator: &LowerBoundedFeeEstimator<F>,
+       ) -> u32 where F::Target: FeeEstimator {
+               fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::OnChainSweep)
+       }
+
+       pub fn get_max_dust_htlc_exposure_msat(&self, limiting_feerate_sat_per_kw: u32) -> u64 {
                match self.config.options.max_dust_htlc_exposure {
                        MaxDustHTLCExposure::FeeRateMultiplier(multiplier) => {
-                               let feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(
-                                       ConfirmationTarget::OnChainSweep) as u64;
-                               feerate_per_kw.saturating_mul(multiplier)
+                               (limiting_feerate_sat_per_kw as u64).saturating_mul(multiplier)
                        },
                        MaxDustHTLCExposure::FixedLimitMsat(limit) => limit,
                }
@@ -2699,7 +2737,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                        feerate_per_kw = cmp::max(feerate_per_kw, feerate);
                }
                let feerate_plus_quarter = feerate_per_kw.checked_mul(1250).map(|v| v / 1000);
-               cmp::max(2530, feerate_plus_quarter.unwrap_or(u32::max_value()))
+               cmp::max(feerate_per_kw + 2530, feerate_plus_quarter.unwrap_or(u32::max_value()))
        }
 
        /// Get forwarding information for the counterparty.
@@ -2707,86 +2745,111 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                self.counterparty_forwarding_info.clone()
        }
 
-       /// Returns a HTLCStats about inbound pending htlcs
-       fn get_inbound_pending_htlc_stats(&self, outbound_feerate_update: Option<u32>) -> HTLCStats {
+       /// Returns a HTLCStats about pending htlcs
+       fn get_pending_htlc_stats(&self, outbound_feerate_update: Option<u32>, dust_exposure_limiting_feerate: u32) -> HTLCStats {
                let context = self;
-               let mut stats = HTLCStats {
-                       pending_htlcs: context.pending_inbound_htlcs.len() as u32,
-                       pending_htlcs_value_msat: 0,
-                       on_counterparty_tx_dust_exposure_msat: 0,
-                       on_holder_tx_dust_exposure_msat: 0,
-                       holding_cell_msat: 0,
-                       on_holder_tx_holding_cell_htlcs_count: 0,
-               };
+               let uses_0_htlc_fee_anchors = self.get_channel_type().supports_anchors_zero_fee_htlc_tx();
 
-               let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
+               let dust_buffer_feerate = context.get_dust_buffer_feerate(outbound_feerate_update);
+               let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if uses_0_htlc_fee_anchors {
                        (0, 0)
                } else {
-                       let dust_buffer_feerate = context.get_dust_buffer_feerate(outbound_feerate_update) as u64;
-                       (dust_buffer_feerate * htlc_timeout_tx_weight(context.get_channel_type()) / 1000,
-                               dust_buffer_feerate * htlc_success_tx_weight(context.get_channel_type()) / 1000)
+                       (dust_buffer_feerate as u64 * htlc_timeout_tx_weight(context.get_channel_type()) / 1000,
+                               dust_buffer_feerate as u64 * htlc_success_tx_weight(context.get_channel_type()) / 1000)
                };
-               let counterparty_dust_limit_timeout_sat = htlc_timeout_dust_limit + context.counterparty_dust_limit_satoshis;
-               let holder_dust_limit_success_sat = htlc_success_dust_limit + context.holder_dust_limit_satoshis;
-               for ref htlc in context.pending_inbound_htlcs.iter() {
-                       stats.pending_htlcs_value_msat += htlc.amount_msat;
-                       if htlc.amount_msat / 1000 < counterparty_dust_limit_timeout_sat {
-                               stats.on_counterparty_tx_dust_exposure_msat += htlc.amount_msat;
-                       }
-                       if htlc.amount_msat / 1000 < holder_dust_limit_success_sat {
-                               stats.on_holder_tx_dust_exposure_msat += htlc.amount_msat;
-                       }
-               }
-               stats
-       }
 
-       /// Returns a HTLCStats about pending outbound htlcs, *including* pending adds in our holding cell.
-       fn get_outbound_pending_htlc_stats(&self, outbound_feerate_update: Option<u32>) -> HTLCStats {
-               let context = self;
-               let mut stats = HTLCStats {
-                       pending_htlcs: context.pending_outbound_htlcs.len() as u32,
-                       pending_htlcs_value_msat: 0,
-                       on_counterparty_tx_dust_exposure_msat: 0,
-                       on_holder_tx_dust_exposure_msat: 0,
-                       holding_cell_msat: 0,
-                       on_holder_tx_holding_cell_htlcs_count: 0,
-               };
+               let mut on_holder_tx_dust_exposure_msat = 0;
+               let mut on_counterparty_tx_dust_exposure_msat = 0;
 
-               let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
-                       (0, 0)
-               } else {
-                       let dust_buffer_feerate = context.get_dust_buffer_feerate(outbound_feerate_update) as u64;
-                       (dust_buffer_feerate * htlc_timeout_tx_weight(context.get_channel_type()) / 1000,
-                               dust_buffer_feerate * htlc_success_tx_weight(context.get_channel_type()) / 1000)
-               };
-               let counterparty_dust_limit_success_sat = htlc_success_dust_limit + context.counterparty_dust_limit_satoshis;
-               let holder_dust_limit_timeout_sat = htlc_timeout_dust_limit + context.holder_dust_limit_satoshis;
-               for ref htlc in context.pending_outbound_htlcs.iter() {
-                       stats.pending_htlcs_value_msat += htlc.amount_msat;
-                       if htlc.amount_msat / 1000 < counterparty_dust_limit_success_sat {
-                               stats.on_counterparty_tx_dust_exposure_msat += htlc.amount_msat;
-                       }
-                       if htlc.amount_msat / 1000 < holder_dust_limit_timeout_sat {
-                               stats.on_holder_tx_dust_exposure_msat += htlc.amount_msat;
+               let mut on_counterparty_tx_offered_nondust_htlcs = 0;
+               let mut on_counterparty_tx_accepted_nondust_htlcs = 0;
+
+               let mut pending_inbound_htlcs_value_msat = 0;
+
+               {
+                       let counterparty_dust_limit_timeout_sat = htlc_timeout_dust_limit + context.counterparty_dust_limit_satoshis;
+                       let holder_dust_limit_success_sat = htlc_success_dust_limit + context.holder_dust_limit_satoshis;
+                       for ref htlc in context.pending_inbound_htlcs.iter() {
+                               pending_inbound_htlcs_value_msat += htlc.amount_msat;
+                               if htlc.amount_msat / 1000 < counterparty_dust_limit_timeout_sat {
+                                       on_counterparty_tx_dust_exposure_msat += htlc.amount_msat;
+                               } else {
+                                       on_counterparty_tx_offered_nondust_htlcs += 1;
+                               }
+                               if htlc.amount_msat / 1000 < holder_dust_limit_success_sat {
+                                       on_holder_tx_dust_exposure_msat += htlc.amount_msat;
+                               }
                        }
                }
 
-               for update in context.holding_cell_htlc_updates.iter() {
-                       if let &HTLCUpdateAwaitingACK::AddHTLC { ref amount_msat, .. } = update {
-                               stats.pending_htlcs += 1;
-                               stats.pending_htlcs_value_msat += amount_msat;
-                               stats.holding_cell_msat += amount_msat;
-                               if *amount_msat / 1000 < counterparty_dust_limit_success_sat {
-                                       stats.on_counterparty_tx_dust_exposure_msat += amount_msat;
-                               }
-                               if *amount_msat / 1000 < holder_dust_limit_timeout_sat {
-                                       stats.on_holder_tx_dust_exposure_msat += amount_msat;
+               let mut pending_outbound_htlcs_value_msat = 0;
+               let mut outbound_holding_cell_msat = 0;
+               let mut on_holder_tx_outbound_holding_cell_htlcs_count = 0;
+               let mut pending_outbound_htlcs = self.pending_outbound_htlcs.len();
+               {
+                       let counterparty_dust_limit_success_sat = htlc_success_dust_limit + context.counterparty_dust_limit_satoshis;
+                       let holder_dust_limit_timeout_sat = htlc_timeout_dust_limit + context.holder_dust_limit_satoshis;
+                       for ref htlc in context.pending_outbound_htlcs.iter() {
+                               pending_outbound_htlcs_value_msat += htlc.amount_msat;
+                               if htlc.amount_msat / 1000 < counterparty_dust_limit_success_sat {
+                                       on_counterparty_tx_dust_exposure_msat += htlc.amount_msat;
                                } else {
-                                       stats.on_holder_tx_holding_cell_htlcs_count += 1;
+                                       on_counterparty_tx_accepted_nondust_htlcs += 1;
+                               }
+                               if htlc.amount_msat / 1000 < holder_dust_limit_timeout_sat {
+                                       on_holder_tx_dust_exposure_msat += htlc.amount_msat;
+                               }
+                       }
+
+                       for update in context.holding_cell_htlc_updates.iter() {
+                               if let &HTLCUpdateAwaitingACK::AddHTLC { ref amount_msat, .. } = update {
+                                       pending_outbound_htlcs += 1;
+                                       pending_outbound_htlcs_value_msat += amount_msat;
+                                       outbound_holding_cell_msat += amount_msat;
+                                       if *amount_msat / 1000 < counterparty_dust_limit_success_sat {
+                                               on_counterparty_tx_dust_exposure_msat += amount_msat;
+                                       } else {
+                                               on_counterparty_tx_accepted_nondust_htlcs += 1;
+                                       }
+                                       if *amount_msat / 1000 < holder_dust_limit_timeout_sat {
+                                               on_holder_tx_dust_exposure_msat += amount_msat;
+                                       } else {
+                                               on_holder_tx_outbound_holding_cell_htlcs_count += 1;
+                                       }
                                }
                        }
                }
-               stats
+
+               // Include any mining "excess" fees in the dust calculation
+               let excess_feerate_opt = outbound_feerate_update
+                       .or(self.pending_update_fee.map(|(fee, _)| fee))
+                       .unwrap_or(self.feerate_per_kw)
+                       .checked_sub(dust_exposure_limiting_feerate);
+               if let Some(excess_feerate) = excess_feerate_opt {
+                       let on_counterparty_tx_nondust_htlcs =
+                               on_counterparty_tx_accepted_nondust_htlcs + on_counterparty_tx_offered_nondust_htlcs;
+                       on_counterparty_tx_dust_exposure_msat +=
+                               commit_tx_fee_msat(excess_feerate, on_counterparty_tx_nondust_htlcs, &self.channel_type);
+                       if !self.channel_type.supports_anchors_zero_fee_htlc_tx() {
+                               on_counterparty_tx_dust_exposure_msat +=
+                                       on_counterparty_tx_accepted_nondust_htlcs as u64 * htlc_success_tx_weight(&self.channel_type)
+                                       * excess_feerate as u64 / 1000;
+                               on_counterparty_tx_dust_exposure_msat +=
+                                       on_counterparty_tx_offered_nondust_htlcs as u64 * htlc_timeout_tx_weight(&self.channel_type)
+                                       * excess_feerate as u64 / 1000;
+                       }
+               }
+
+               HTLCStats {
+                       pending_inbound_htlcs: self.pending_inbound_htlcs.len(),
+                       pending_outbound_htlcs,
+                       pending_inbound_htlcs_value_msat,
+                       pending_outbound_htlcs_value_msat,
+                       on_counterparty_tx_dust_exposure_msat,
+                       on_holder_tx_dust_exposure_msat,
+                       outbound_holding_cell_msat,
+                       on_holder_tx_outbound_holding_cell_htlcs_count,
+               }
        }
 
        /// Returns information on all pending inbound HTLCs.
@@ -2891,9 +2954,11 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
        where F::Target: FeeEstimator
        {
                let context = &self;
-               // Note that we have to handle overflow due to the above case.
-               let inbound_stats = context.get_inbound_pending_htlc_stats(None);
-               let outbound_stats = context.get_outbound_pending_htlc_stats(None);
+               // Note that we have to handle overflow due to the case mentioned in the docs in general
+               // here.
+
+               let dust_exposure_limiting_feerate = self.get_dust_exposure_limiting_feerate(&fee_estimator);
+               let htlc_stats = context.get_pending_htlc_stats(None, dust_exposure_limiting_feerate);
 
                let mut balance_msat = context.value_to_self_msat;
                for ref htlc in context.pending_inbound_htlcs.iter() {
@@ -2901,10 +2966,10 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                                balance_msat += htlc.amount_msat;
                        }
                }
-               balance_msat -= outbound_stats.pending_htlcs_value_msat;
+               balance_msat -= htlc_stats.pending_outbound_htlcs_value_msat;
 
                let outbound_capacity_msat = context.value_to_self_msat
-                               .saturating_sub(outbound_stats.pending_htlcs_value_msat)
+                               .saturating_sub(htlc_stats.pending_outbound_htlcs_value_msat)
                                .saturating_sub(
                                        context.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) * 1000);
 
@@ -2964,7 +3029,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
 
                        let holder_selected_chan_reserve_msat = context.holder_selected_channel_reserve_satoshis * 1000;
                        let remote_balance_msat = (context.channel_value_satoshis * 1000 - context.value_to_self_msat)
-                               .saturating_sub(inbound_stats.pending_htlcs_value_msat);
+                               .saturating_sub(htlc_stats.pending_inbound_htlcs_value_msat);
 
                        if remote_balance_msat < max_reserved_commit_tx_fee_msat + holder_selected_chan_reserve_msat + anchor_outputs_value_msat {
                                // If another HTLC's fee would reduce the remote's balance below the reserve limit
@@ -2981,7 +3046,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                // send above the dust limit (as the router can always overpay to meet the dust limit).
                let mut remaining_msat_below_dust_exposure_limit = None;
                let mut dust_exposure_dust_limit_msat = 0;
-               let max_dust_htlc_exposure_msat = context.get_max_dust_htlc_exposure_msat(fee_estimator);
+               let max_dust_htlc_exposure_msat = context.get_max_dust_htlc_exposure_msat(dust_exposure_limiting_feerate);
 
                let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
                        (context.counterparty_dust_limit_satoshis, context.holder_dust_limit_satoshis)
@@ -2990,18 +3055,32 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                        (context.counterparty_dust_limit_satoshis + dust_buffer_feerate * htlc_success_tx_weight(context.get_channel_type()) / 1000,
                         context.holder_dust_limit_satoshis       + dust_buffer_feerate * htlc_timeout_tx_weight(context.get_channel_type()) / 1000)
                };
-               let on_counterparty_dust_htlc_exposure_msat = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat;
-               if on_counterparty_dust_htlc_exposure_msat as i64 + htlc_success_dust_limit as i64 * 1000 - 1 > max_dust_htlc_exposure_msat.try_into().unwrap_or(i64::max_value()) {
+
+               let excess_feerate_opt = self.feerate_per_kw.checked_sub(dust_exposure_limiting_feerate);
+               if let Some(excess_feerate) = excess_feerate_opt {
+                       let htlc_dust_exposure_msat =
+                               per_outbound_htlc_counterparty_commit_tx_fee_msat(excess_feerate, &context.channel_type);
+                       let nondust_htlc_counterparty_tx_dust_exposure =
+                               htlc_stats.on_counterparty_tx_dust_exposure_msat.saturating_add(htlc_dust_exposure_msat);
+                       if nondust_htlc_counterparty_tx_dust_exposure > max_dust_htlc_exposure_msat {
+                               // If adding an extra HTLC would put us over the dust limit in total fees, we cannot
+                               // send any non-dust HTLCs.
+                               available_capacity_msat = cmp::min(available_capacity_msat, htlc_success_dust_limit * 1000);
+                       }
+               }
+
+               if htlc_stats.on_counterparty_tx_dust_exposure_msat.saturating_add(htlc_success_dust_limit * 1000) > max_dust_htlc_exposure_msat.saturating_add(1) {
+                       // Note that we don't use the `counterparty_tx_dust_exposure` (with
+                       // `htlc_dust_exposure_msat`) here as it only applies to non-dust HTLCs.
                        remaining_msat_below_dust_exposure_limit =
-                               Some(max_dust_htlc_exposure_msat.saturating_sub(on_counterparty_dust_htlc_exposure_msat));
+                               Some(max_dust_htlc_exposure_msat.saturating_sub(htlc_stats.on_counterparty_tx_dust_exposure_msat));
                        dust_exposure_dust_limit_msat = cmp::max(dust_exposure_dust_limit_msat, htlc_success_dust_limit * 1000);
                }
 
-               let on_holder_dust_htlc_exposure_msat = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat;
-               if on_holder_dust_htlc_exposure_msat as i64 + htlc_timeout_dust_limit as i64 * 1000 - 1 > max_dust_htlc_exposure_msat.try_into().unwrap_or(i64::max_value()) {
+               if htlc_stats.on_holder_tx_dust_exposure_msat as i64 + htlc_timeout_dust_limit as i64 * 1000 - 1 > max_dust_htlc_exposure_msat.try_into().unwrap_or(i64::max_value()) {
                        remaining_msat_below_dust_exposure_limit = Some(cmp::min(
                                remaining_msat_below_dust_exposure_limit.unwrap_or(u64::max_value()),
-                               max_dust_htlc_exposure_msat.saturating_sub(on_holder_dust_htlc_exposure_msat)));
+                               max_dust_htlc_exposure_msat.saturating_sub(htlc_stats.on_holder_tx_dust_exposure_msat)));
                        dust_exposure_dust_limit_msat = cmp::max(dust_exposure_dust_limit_msat, htlc_timeout_dust_limit * 1000);
                }
 
@@ -3014,16 +3093,16 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                }
 
                available_capacity_msat = cmp::min(available_capacity_msat,
-                       context.counterparty_max_htlc_value_in_flight_msat - outbound_stats.pending_htlcs_value_msat);
+                       context.counterparty_max_htlc_value_in_flight_msat - htlc_stats.pending_outbound_htlcs_value_msat);
 
-               if outbound_stats.pending_htlcs + 1 > context.counterparty_max_accepted_htlcs as u32 {
+               if htlc_stats.pending_outbound_htlcs + 1 > context.counterparty_max_accepted_htlcs as usize {
                        available_capacity_msat = 0;
                }
 
                AvailableBalances {
                        inbound_capacity_msat: cmp::max(context.channel_value_satoshis as i64 * 1000
                                        - context.value_to_self_msat as i64
-                                       - context.get_inbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
+                                       - htlc_stats.pending_inbound_htlcs_value_msat as i64
                                        - context.holder_selected_channel_reserve_satoshis as i64 * 1000,
                                0) as u64,
                        outbound_capacity_msat,
@@ -3470,7 +3549,7 @@ pub(crate) fn get_legacy_default_holder_selected_channel_reserve_satoshis(channe
 ///
 /// This is used both for outbound and inbound channels and has lower bound
 /// of `dust_limit_satoshis`.
-#[cfg(dual_funding)]
+#[cfg(any(dual_funding, splicing))]
 fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satoshis: u64) -> u64 {
        // Fixed at 1% of channel value by spec.
        let (q, _) = channel_value_satoshis.overflowing_div(100);
@@ -3492,8 +3571,19 @@ pub(crate) fn commit_tx_fee_msat(feerate_per_kw: u32, num_htlcs: usize, channel_
        (commitment_tx_base_weight(channel_type_features) + num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC) * feerate_per_kw as u64 / 1000 * 1000
 }
 
+pub(crate) fn per_outbound_htlc_counterparty_commit_tx_fee_msat(feerate_per_kw: u32, channel_type_features: &ChannelTypeFeatures) -> u64 {
+       // Note that we need to divide before multiplying to round properly,
+       // since the lowest denomination of bitcoin on-chain is the satoshi.
+       let commitment_tx_fee = COMMITMENT_TX_WEIGHT_PER_HTLC * feerate_per_kw as u64 / 1000 * 1000;
+       if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
+               commitment_tx_fee + htlc_success_tx_weight(channel_type_features) * feerate_per_kw as u64 / 1000
+       } else {
+               commitment_tx_fee
+       }
+}
+
 /// Context for dual-funded channels.
-#[cfg(dual_funding)]
+#[cfg(any(dual_funding, splicing))]
 pub(super) struct DualFundingChannelContext {
        /// The amount in satoshis we will be contributing to the channel.
        pub our_funding_satoshis: u64,
@@ -3510,7 +3600,7 @@ pub(super) struct DualFundingChannelContext {
 // Counterparty designates channel data owned by the another channel participant entity.
 pub(super) struct Channel<SP: Deref> where SP::Target: SignerProvider {
        pub context: ChannelContext<SP>,
-       #[cfg(dual_funding)]
+       #[cfg(any(dual_funding, splicing))]
        pub dual_funding_channel_context: Option<DualFundingChannelContext>,
 }
 
@@ -3581,7 +3671,7 @@ impl FailHTLCMessageName for msgs::UpdateFailMalformedHTLC {
 
 impl<SP: Deref> Channel<SP> where
        SP::Target: SignerProvider,
-       <SP::Target as SignerProvider>::EcdsaSigner: WriteableEcdsaChannelSigner
+       <SP::Target as SignerProvider>::EcdsaSigner: EcdsaChannelSigner
 {
        fn check_remote_fee<F: Deref, L: Deref>(
                channel_type: &ChannelTypeFeatures, fee_estimator: &LowerBoundedFeeEstimator<F>,
@@ -4089,20 +4179,13 @@ impl<SP: Deref> Channel<SP> where
                Ok(self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height, logger))
        }
 
-       pub fn update_add_htlc<F, FE: Deref, L: Deref>(
-               &mut self, msg: &msgs::UpdateAddHTLC, mut pending_forward_status: PendingHTLCStatus,
-               create_pending_htlc_status: F, fee_estimator: &LowerBoundedFeeEstimator<FE>, logger: &L
-       ) -> Result<(), ChannelError>
-       where F: for<'a> Fn(&'a Self, PendingHTLCStatus, u16) -> PendingHTLCStatus,
-               FE::Target: FeeEstimator, L::Target: Logger,
-       {
+       pub fn update_add_htlc<F: Deref>(
+               &mut self, msg: &msgs::UpdateAddHTLC, pending_forward_status: PendingHTLCStatus,
+               fee_estimator: &LowerBoundedFeeEstimator<F>,
+       ) -> Result<(), ChannelError> where F::Target: FeeEstimator {
                if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
                        return Err(ChannelError::Close("Got add HTLC message when channel was not in an operational state".to_owned()));
                }
-               // We can't accept HTLCs sent after we've sent a shutdown.
-               if self.context.channel_state.is_local_shutdown_sent() {
-                       pending_forward_status = create_pending_htlc_status(self, pending_forward_status, 0x4000|8);
-               }
                // If the remote has sent a shutdown prior to adding this HTLC, then they are in violation of the spec.
                if self.context.channel_state.is_remote_shutdown_sent() {
                        return Err(ChannelError::Close("Got add HTLC message when channel was not in an operational state".to_owned()));
@@ -4120,12 +4203,12 @@ impl<SP: Deref> Channel<SP> where
                        return Err(ChannelError::Close(format!("Remote side tried to send less than our minimum HTLC value. Lower limit: ({}). Actual: ({})", self.context.holder_htlc_minimum_msat, msg.amount_msat)));
                }
 
-               let inbound_stats = self.context.get_inbound_pending_htlc_stats(None);
-               let outbound_stats = self.context.get_outbound_pending_htlc_stats(None);
-               if inbound_stats.pending_htlcs + 1 > self.context.holder_max_accepted_htlcs as u32 {
+               let dust_exposure_limiting_feerate = self.context.get_dust_exposure_limiting_feerate(&fee_estimator);
+               let htlc_stats = self.context.get_pending_htlc_stats(None, dust_exposure_limiting_feerate);
+               if htlc_stats.pending_inbound_htlcs + 1 > self.context.holder_max_accepted_htlcs as usize {
                        return Err(ChannelError::Close(format!("Remote tried to push more than our max accepted HTLCs ({})", self.context.holder_max_accepted_htlcs)));
                }
-               if inbound_stats.pending_htlcs_value_msat + msg.amount_msat > self.context.holder_max_htlc_value_in_flight_msat {
+               if htlc_stats.pending_inbound_htlcs_value_msat + msg.amount_msat > self.context.holder_max_htlc_value_in_flight_msat {
                        return Err(ChannelError::Close(format!("Remote HTLC add would put them over our max HTLC value ({})", self.context.holder_max_htlc_value_in_flight_msat)));
                }
 
@@ -4150,36 +4233,8 @@ impl<SP: Deref> Channel<SP> where
                        }
                }
 
-               let max_dust_htlc_exposure_msat = self.context.get_max_dust_htlc_exposure_msat(fee_estimator);
-               let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
-                       (0, 0)
-               } else {
-                       let dust_buffer_feerate = self.context.get_dust_buffer_feerate(None) as u64;
-                       (dust_buffer_feerate * htlc_timeout_tx_weight(self.context.get_channel_type()) / 1000,
-                               dust_buffer_feerate * htlc_success_tx_weight(self.context.get_channel_type()) / 1000)
-               };
-               let exposure_dust_limit_timeout_sats = htlc_timeout_dust_limit + self.context.counterparty_dust_limit_satoshis;
-               if msg.amount_msat / 1000 < exposure_dust_limit_timeout_sats {
-                       let on_counterparty_tx_dust_htlc_exposure_msat = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat + msg.amount_msat;
-                       if on_counterparty_tx_dust_htlc_exposure_msat > max_dust_htlc_exposure_msat {
-                               log_info!(logger, "Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx",
-                                       on_counterparty_tx_dust_htlc_exposure_msat, max_dust_htlc_exposure_msat);
-                               pending_forward_status = create_pending_htlc_status(self, pending_forward_status, 0x1000|7);
-                       }
-               }
-
-               let exposure_dust_limit_success_sats = htlc_success_dust_limit + self.context.holder_dust_limit_satoshis;
-               if msg.amount_msat / 1000 < exposure_dust_limit_success_sats {
-                       let on_holder_tx_dust_htlc_exposure_msat = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat + msg.amount_msat;
-                       if on_holder_tx_dust_htlc_exposure_msat > max_dust_htlc_exposure_msat {
-                               log_info!(logger, "Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx",
-                                       on_holder_tx_dust_htlc_exposure_msat, max_dust_htlc_exposure_msat);
-                               pending_forward_status = create_pending_htlc_status(self, pending_forward_status, 0x1000|7);
-                       }
-               }
-
                let pending_value_to_self_msat =
-                       self.context.value_to_self_msat + inbound_stats.pending_htlcs_value_msat - removed_outbound_total_msat;
+                       self.context.value_to_self_msat + htlc_stats.pending_inbound_htlcs_value_msat - removed_outbound_total_msat;
                let pending_remote_value_msat =
                        self.context.channel_value_satoshis * 1000 - pending_value_to_self_msat;
                if pending_remote_value_msat < msg.amount_msat {
@@ -4211,23 +4266,7 @@ impl<SP: Deref> Channel<SP> where
                } else {
                        0
                };
-               if !self.context.is_outbound() {
-                       // `Some(())` is for the fee spike buffer we keep for the remote. This deviates from
-                       // the spec because the fee spike buffer requirement doesn't exist on the receiver's
-                       // side, only on the sender's. Note that with anchor outputs we are no longer as
-                       // sensitive to fee spikes, so we need to account for them.
-                       let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered);
-                       let mut remote_fee_cost_incl_stuck_buffer_msat = self.context.next_remote_commit_tx_fee_msat(htlc_candidate, Some(()));
-                       if !self.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
-                               remote_fee_cost_incl_stuck_buffer_msat *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
-                       }
-                       if pending_remote_value_msat.saturating_sub(msg.amount_msat).saturating_sub(self.context.holder_selected_channel_reserve_satoshis * 1000).saturating_sub(anchor_outputs_value_msat) < remote_fee_cost_incl_stuck_buffer_msat {
-                               // Note that if the pending_forward_status is not updated here, then it's because we're already failing
-                               // the HTLC, i.e. its status is already set to failing.
-                               log_info!(logger, "Attempting to fail HTLC due to fee spike buffer violation in channel {}. Rebalancing is required.", &self.context.channel_id());
-                               pending_forward_status = create_pending_htlc_status(self, pending_forward_status, 0x1000|7);
-                       }
-               } else {
+               if self.context.is_outbound() {
                        // Check that they won't violate our local required channel reserve by adding this HTLC.
                        let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered);
                        let local_commit_tx_fee_msat = self.context.next_local_commit_tx_fee_msat(htlc_candidate, None);
@@ -4255,7 +4294,9 @@ impl<SP: Deref> Channel<SP> where
                        amount_msat: msg.amount_msat,
                        payment_hash: msg.payment_hash,
                        cltv_expiry: msg.cltv_expiry,
-                       state: InboundHTLCState::RemoteAnnounced(pending_forward_status),
+                       state: InboundHTLCState::RemoteAnnounced(InboundHTLCResolution::Resolved {
+                               pending_htlc_status: pending_forward_status
+                       }),
                });
                Ok(())
        }
@@ -4419,7 +4460,7 @@ impl<SP: Deref> Channel<SP> where
 
                                let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &self.context.channel_type, &keys);
                                let htlc_sighashtype = if self.context.channel_type.supports_anchors_zero_fee_htlc_tx() { 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 htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).p2wsh_signature_hash(0, &htlc_redeemscript, htlc.to_bitcoin_amount(), htlc_sighashtype).unwrap()[..]);
                                log_trace!(logger, "Checking HTLC tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} in channel {}.",
                                        log_bytes!(msg.htlc_signatures[idx].serialize_compact()[..]), log_bytes!(keys.countersignatory_htlc_key.to_public_key().serialize()),
                                        encode::serialize_hex(&htlc_tx), log_bytes!(htlc_sighash[..]), encode::serialize_hex(&htlc_redeemscript), &self.context.channel_id());
@@ -4461,13 +4502,13 @@ impl<SP: Deref> Channel<SP> where
                }
 
                for htlc in self.context.pending_inbound_htlcs.iter_mut() {
-                       let new_forward = if let &InboundHTLCState::RemoteAnnounced(ref forward_info) = &htlc.state {
-                               Some(forward_info.clone())
+                       let htlc_resolution = if let &InboundHTLCState::RemoteAnnounced(ref resolution) = &htlc.state {
+                               Some(resolution.clone())
                        } else { None };
-                       if let Some(forward_info) = new_forward {
+                       if let Some(htlc_resolution) = htlc_resolution {
                                log_trace!(logger, "Updating HTLC {} to AwaitingRemoteRevokeToAnnounce due to commitment_signed in channel {}.",
                                        &htlc.payment_hash, &self.context.channel_id);
-                               htlc.state = InboundHTLCState::AwaitingRemoteRevokeToAnnounce(forward_info);
+                               htlc.state = InboundHTLCState::AwaitingRemoteRevokeToAnnounce(htlc_resolution);
                                need_commitment = true;
                        }
                }
@@ -4777,6 +4818,7 @@ impl<SP: Deref> Channel<SP> where
 
                log_trace!(logger, "Updating HTLCs on receipt of RAA in channel {}...", &self.context.channel_id());
                let mut to_forward_infos = Vec::new();
+               let mut pending_update_adds = Vec::new();
                let mut revoked_htlcs = Vec::new();
                let mut finalized_claimed_htlcs = Vec::new();
                let mut update_fail_htlcs = Vec::new();
@@ -4824,29 +4866,37 @@ impl<SP: Deref> Channel<SP> where
                                        let mut state = InboundHTLCState::Committed;
                                        mem::swap(&mut state, &mut htlc.state);
 
-                                       if let InboundHTLCState::AwaitingRemoteRevokeToAnnounce(forward_info) = state {
+                                       if let InboundHTLCState::AwaitingRemoteRevokeToAnnounce(resolution) = state {
                                                log_trace!(logger, " ...promoting inbound AwaitingRemoteRevokeToAnnounce {} to AwaitingAnnouncedRemoteRevoke", &htlc.payment_hash);
-                                               htlc.state = InboundHTLCState::AwaitingAnnouncedRemoteRevoke(forward_info);
+                                               htlc.state = InboundHTLCState::AwaitingAnnouncedRemoteRevoke(resolution);
                                                require_commitment = true;
-                                       } else if let InboundHTLCState::AwaitingAnnouncedRemoteRevoke(forward_info) = state {
-                                               match forward_info {
-                                                       PendingHTLCStatus::Fail(fail_msg) => {
-                                                               log_trace!(logger, " ...promoting inbound AwaitingAnnouncedRemoteRevoke {} to LocalRemoved due to PendingHTLCStatus indicating failure", &htlc.payment_hash);
-                                                               require_commitment = true;
-                                                               match fail_msg {
-                                                                       HTLCFailureMsg::Relay(msg) => {
-                                                                               htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(msg.reason.clone()));
-                                                                               update_fail_htlcs.push(msg)
-                                                                       },
-                                                                       HTLCFailureMsg::Malformed(msg) => {
-                                                                               htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailMalformed((msg.sha256_of_onion, msg.failure_code)));
-                                                                               update_fail_malformed_htlcs.push(msg)
+                                       } else if let InboundHTLCState::AwaitingAnnouncedRemoteRevoke(resolution) = state {
+                                               match resolution {
+                                                       InboundHTLCResolution::Resolved { pending_htlc_status } =>
+                                                               match pending_htlc_status {
+                                                                       PendingHTLCStatus::Fail(fail_msg) => {
+                                                                               log_trace!(logger, " ...promoting inbound AwaitingAnnouncedRemoteRevoke {} to LocalRemoved due to PendingHTLCStatus indicating failure", &htlc.payment_hash);
+                                                                               require_commitment = true;
+                                                                               match fail_msg {
+                                                                                       HTLCFailureMsg::Relay(msg) => {
+                                                                                               htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(msg.reason.clone()));
+                                                                                               update_fail_htlcs.push(msg)
+                                                                                       },
+                                                                                       HTLCFailureMsg::Malformed(msg) => {
+                                                                                               htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailMalformed((msg.sha256_of_onion, msg.failure_code)));
+                                                                                               update_fail_malformed_htlcs.push(msg)
+                                                                                       },
+                                                                               }
                                                                        },
+                                                                       PendingHTLCStatus::Forward(forward_info) => {
+                                                                               log_trace!(logger, " ...promoting inbound AwaitingAnnouncedRemoteRevoke {} to Committed, attempting to forward", &htlc.payment_hash);
+                                                                               to_forward_infos.push((forward_info, htlc.htlc_id));
+                                                                               htlc.state = InboundHTLCState::Committed;
+                                                                       }
                                                                }
-                                                       },
-                                                       PendingHTLCStatus::Forward(forward_info) => {
+                                                       InboundHTLCResolution::Pending { update_add_htlc } => {
                                                                log_trace!(logger, " ...promoting inbound AwaitingAnnouncedRemoteRevoke {} to Committed", &htlc.payment_hash);
-                                                               to_forward_infos.push((forward_info, htlc.htlc_id));
+                                                               pending_update_adds.push(update_add_htlc);
                                                                htlc.state = InboundHTLCState::Committed;
                                                        }
                                                }
@@ -4907,6 +4957,8 @@ impl<SP: Deref> Channel<SP> where
                        }
                }
 
+               self.context.monitor_pending_update_adds.append(&mut pending_update_adds);
+
                if self.context.channel_state.is_monitor_update_in_progress() {
                        // We can't actually generate a new commitment transaction (incl by freeing holding
                        // cells) while we can't update the monitor, so we just return what we have.
@@ -5004,12 +5056,12 @@ impl<SP: Deref> Channel<SP> where
                }
 
                // Before proposing a feerate update, check that we can actually afford the new fee.
-               let inbound_stats = self.context.get_inbound_pending_htlc_stats(Some(feerate_per_kw));
-               let outbound_stats = self.context.get_outbound_pending_htlc_stats(Some(feerate_per_kw));
+               let dust_exposure_limiting_feerate = self.context.get_dust_exposure_limiting_feerate(&fee_estimator);
+               let htlc_stats = self.context.get_pending_htlc_stats(Some(feerate_per_kw), dust_exposure_limiting_feerate);
                let keys = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number);
                let commitment_stats = self.context.build_commitment_transaction(self.context.cur_holder_commitment_transaction_number, &keys, true, true, logger);
-               let buffer_fee_msat = commit_tx_fee_sat(feerate_per_kw, commitment_stats.num_nondust_htlcs + outbound_stats.on_holder_tx_holding_cell_htlcs_count as usize + CONCURRENT_INBOUND_HTLC_FEE_BUFFER as usize, self.context.get_channel_type()) * 1000;
-               let holder_balance_msat = commitment_stats.local_balance_msat - outbound_stats.holding_cell_msat;
+               let buffer_fee_msat = commit_tx_fee_sat(feerate_per_kw, commitment_stats.num_nondust_htlcs + htlc_stats.on_holder_tx_outbound_holding_cell_htlcs_count as usize + CONCURRENT_INBOUND_HTLC_FEE_BUFFER as usize, self.context.get_channel_type()) * 1000;
+               let holder_balance_msat = commitment_stats.local_balance_msat - htlc_stats.outbound_holding_cell_msat;
                if holder_balance_msat < buffer_fee_msat  + self.context.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 {
                        //TODO: auto-close after a number of failures?
                        log_debug!(logger, "Cannot afford to send new feerate at {}", feerate_per_kw);
@@ -5017,14 +5069,12 @@ impl<SP: Deref> Channel<SP> where
                }
 
                // Note, we evaluate pending htlc "preemptive" trimmed-to-dust threshold at the proposed `feerate_per_kw`.
-               let holder_tx_dust_exposure = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat;
-               let counterparty_tx_dust_exposure = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat;
-               let max_dust_htlc_exposure_msat = self.context.get_max_dust_htlc_exposure_msat(fee_estimator);
-               if holder_tx_dust_exposure > max_dust_htlc_exposure_msat {
+               let max_dust_htlc_exposure_msat = self.context.get_max_dust_htlc_exposure_msat(dust_exposure_limiting_feerate);
+               if htlc_stats.on_holder_tx_dust_exposure_msat > max_dust_htlc_exposure_msat {
                        log_debug!(logger, "Cannot afford to send new feerate at {} without infringing max dust htlc exposure", feerate_per_kw);
                        return None;
                }
-               if counterparty_tx_dust_exposure > max_dust_htlc_exposure_msat {
+               if htlc_stats.on_counterparty_tx_dust_exposure_msat > max_dust_htlc_exposure_msat {
                        log_debug!(logger, "Cannot afford to send new feerate at {} without infringing max dust htlc exposure", feerate_per_kw);
                        return None;
                }
@@ -5207,13 +5257,16 @@ impl<SP: Deref> Channel<SP> where
                mem::swap(&mut failed_htlcs, &mut self.context.monitor_pending_failures);
                let mut finalized_claimed_htlcs = Vec::new();
                mem::swap(&mut finalized_claimed_htlcs, &mut self.context.monitor_pending_finalized_fulfills);
+               let mut pending_update_adds = Vec::new();
+               mem::swap(&mut pending_update_adds, &mut self.context.monitor_pending_update_adds);
 
                if self.context.channel_state.is_peer_disconnected() {
                        self.context.monitor_pending_revoke_and_ack = false;
                        self.context.monitor_pending_commitment_signed = false;
                        return MonitorRestoreUpdates {
                                raa: None, commitment_update: None, order: RAACommitmentOrder::RevokeAndACKFirst,
-                               accepted_htlcs, failed_htlcs, finalized_claimed_htlcs, funding_broadcastable, channel_ready, announcement_sigs
+                               accepted_htlcs, failed_htlcs, finalized_claimed_htlcs, pending_update_adds,
+                               funding_broadcastable, channel_ready, announcement_sigs
                        };
                }
 
@@ -5235,7 +5288,8 @@ impl<SP: Deref> Channel<SP> where
                        if commitment_update.is_some() { "a" } else { "no" }, if raa.is_some() { "an" } else { "no" },
                        match order { RAACommitmentOrder::CommitmentFirst => "commitment", RAACommitmentOrder::RevokeAndACKFirst => "RAA"});
                MonitorRestoreUpdates {
-                       raa, commitment_update, order, accepted_htlcs, failed_htlcs, finalized_claimed_htlcs, funding_broadcastable, channel_ready, announcement_sigs
+                       raa, commitment_update, order, accepted_htlcs, failed_htlcs, finalized_claimed_htlcs,
+                       pending_update_adds, funding_broadcastable, channel_ready, announcement_sigs
                }
        }
 
@@ -5253,20 +5307,16 @@ impl<SP: Deref> Channel<SP> where
                self.context.pending_update_fee = Some((msg.feerate_per_kw, FeeUpdateState::RemoteAnnounced));
                self.context.update_time_counter += 1;
                // Check that we won't be pushed over our dust exposure limit by the feerate increase.
-               if !self.context.channel_type.supports_anchors_zero_fee_htlc_tx() {
-                       let inbound_stats = self.context.get_inbound_pending_htlc_stats(None);
-                       let outbound_stats = self.context.get_outbound_pending_htlc_stats(None);
-                       let holder_tx_dust_exposure = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat;
-                       let counterparty_tx_dust_exposure = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat;
-                       let max_dust_htlc_exposure_msat = self.context.get_max_dust_htlc_exposure_msat(fee_estimator);
-                       if holder_tx_dust_exposure > max_dust_htlc_exposure_msat {
-                               return Err(ChannelError::Close(format!("Peer sent update_fee with a feerate ({}) which may over-expose us to dust-in-flight on our own transactions (totaling {} msat)",
-                                       msg.feerate_per_kw, holder_tx_dust_exposure)));
-                       }
-                       if counterparty_tx_dust_exposure > max_dust_htlc_exposure_msat {
-                               return Err(ChannelError::Close(format!("Peer sent update_fee with a feerate ({}) which may over-expose us to dust-in-flight on our counterparty's transactions (totaling {} msat)",
-                                       msg.feerate_per_kw, counterparty_tx_dust_exposure)));
-                       }
+               let dust_exposure_limiting_feerate = self.context.get_dust_exposure_limiting_feerate(&fee_estimator);
+               let htlc_stats = self.context.get_pending_htlc_stats(None, dust_exposure_limiting_feerate);
+               let max_dust_htlc_exposure_msat = self.context.get_max_dust_htlc_exposure_msat(dust_exposure_limiting_feerate);
+               if htlc_stats.on_holder_tx_dust_exposure_msat > max_dust_htlc_exposure_msat {
+                       return Err(ChannelError::Close(format!("Peer sent update_fee with a feerate ({}) which may over-expose us to dust-in-flight on our own transactions (totaling {} msat)",
+                               msg.feerate_per_kw, htlc_stats.on_holder_tx_dust_exposure_msat)));
+               }
+               if htlc_stats.on_counterparty_tx_dust_exposure_msat > max_dust_htlc_exposure_msat {
+                       return Err(ChannelError::Close(format!("Peer sent update_fee with a feerate ({}) which may over-expose us to dust-in-flight on our counterparty's transactions (totaling {} msat)",
+                               msg.feerate_per_kw, htlc_stats.on_counterparty_tx_dust_exposure_msat)));
                }
                Ok(())
        }
@@ -5918,7 +5968,7 @@ impl<SP: Deref> Channel<SP> where
                };
 
                for outp in closing_tx.trust().built_transaction().output.iter() {
-                       if !outp.script_pubkey.is_witness_program() && outp.value < MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS {
+                       if !outp.script_pubkey.is_witness_program() && outp.value < Amount::from_sat(MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS) {
                                return Err(ChannelError::Close("Remote sent us a closing_signed with a dust output. Always use segwit closing scripts!".to_owned()));
                        }
                }
@@ -6099,6 +6149,96 @@ impl<SP: Deref> Channel<SP> where
                        })
        }
 
+       pub fn can_accept_incoming_htlc<F: Deref, L: Deref>(
+               &self, msg: &msgs::UpdateAddHTLC, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: L
+       ) -> Result<(), (&'static str, u16)>
+       where
+               F::Target: FeeEstimator,
+               L::Target: Logger
+       {
+               if self.context.channel_state.is_local_shutdown_sent() {
+                       return Err(("Shutdown was already sent", 0x4000|8))
+               }
+
+               let dust_exposure_limiting_feerate = self.context.get_dust_exposure_limiting_feerate(&fee_estimator);
+               let htlc_stats = self.context.get_pending_htlc_stats(None, dust_exposure_limiting_feerate);
+               let max_dust_htlc_exposure_msat = self.context.get_max_dust_htlc_exposure_msat(dust_exposure_limiting_feerate);
+               let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
+                       (0, 0)
+               } else {
+                       let dust_buffer_feerate = self.context.get_dust_buffer_feerate(None) as u64;
+                       (dust_buffer_feerate * htlc_timeout_tx_weight(self.context.get_channel_type()) / 1000,
+                               dust_buffer_feerate * htlc_success_tx_weight(self.context.get_channel_type()) / 1000)
+               };
+               let exposure_dust_limit_timeout_sats = htlc_timeout_dust_limit + self.context.counterparty_dust_limit_satoshis;
+               if msg.amount_msat / 1000 < exposure_dust_limit_timeout_sats {
+                       let on_counterparty_tx_dust_htlc_exposure_msat = htlc_stats.on_counterparty_tx_dust_exposure_msat + msg.amount_msat;
+                       if on_counterparty_tx_dust_htlc_exposure_msat > max_dust_htlc_exposure_msat {
+                               log_info!(logger, "Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx",
+                                       on_counterparty_tx_dust_htlc_exposure_msat, max_dust_htlc_exposure_msat);
+                               return Err(("Exceeded our dust exposure limit on counterparty commitment tx", 0x1000|7))
+                       }
+               } else {
+                       let htlc_dust_exposure_msat =
+                               per_outbound_htlc_counterparty_commit_tx_fee_msat(self.context.feerate_per_kw, &self.context.channel_type);
+                       let counterparty_tx_dust_exposure =
+                               htlc_stats.on_counterparty_tx_dust_exposure_msat.saturating_add(htlc_dust_exposure_msat);
+                       if counterparty_tx_dust_exposure > max_dust_htlc_exposure_msat {
+                               log_info!(logger, "Cannot accept value that would put our exposure to tx fee dust at {} over the limit {} on counterparty commitment tx",
+                                       counterparty_tx_dust_exposure, max_dust_htlc_exposure_msat);
+                               return Err(("Exceeded our tx fee dust exposure limit on counterparty commitment tx", 0x1000|7))
+                       }
+               }
+
+               let exposure_dust_limit_success_sats = htlc_success_dust_limit + self.context.holder_dust_limit_satoshis;
+               if msg.amount_msat / 1000 < exposure_dust_limit_success_sats {
+                       let on_holder_tx_dust_htlc_exposure_msat = htlc_stats.on_holder_tx_dust_exposure_msat + msg.amount_msat;
+                       if on_holder_tx_dust_htlc_exposure_msat > max_dust_htlc_exposure_msat {
+                               log_info!(logger, "Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx",
+                                       on_holder_tx_dust_htlc_exposure_msat, max_dust_htlc_exposure_msat);
+                               return Err(("Exceeded our dust exposure limit on holder commitment tx", 0x1000|7))
+                       }
+               }
+
+               let anchor_outputs_value_msat = if self.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
+                       ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000
+               } else {
+                       0
+               };
+
+               let mut removed_outbound_total_msat = 0;
+               for ref htlc in self.context.pending_outbound_htlcs.iter() {
+                       if let OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_)) = htlc.state {
+                               removed_outbound_total_msat += htlc.amount_msat;
+                       } else if let OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) = htlc.state {
+                               removed_outbound_total_msat += htlc.amount_msat;
+                       }
+               }
+
+               let pending_value_to_self_msat =
+                       self.context.value_to_self_msat + htlc_stats.pending_inbound_htlcs_value_msat - removed_outbound_total_msat;
+               let pending_remote_value_msat =
+                       self.context.channel_value_satoshis * 1000 - pending_value_to_self_msat;
+
+               if !self.context.is_outbound() {
+                       // `Some(())` is for the fee spike buffer we keep for the remote. This deviates from
+                       // the spec because the fee spike buffer requirement doesn't exist on the receiver's
+                       // side, only on the sender's. Note that with anchor outputs we are no longer as
+                       // sensitive to fee spikes, so we need to account for them.
+                       let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered);
+                       let mut remote_fee_cost_incl_stuck_buffer_msat = self.context.next_remote_commit_tx_fee_msat(htlc_candidate, Some(()));
+                       if !self.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
+                               remote_fee_cost_incl_stuck_buffer_msat *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
+                       }
+                       if pending_remote_value_msat.saturating_sub(msg.amount_msat).saturating_sub(self.context.holder_selected_channel_reserve_satoshis * 1000).saturating_sub(anchor_outputs_value_msat) < remote_fee_cost_incl_stuck_buffer_msat {
+                               log_info!(logger, "Attempting to fail HTLC due to fee spike buffer violation in channel {}. Rebalancing is required.", &self.context.channel_id());
+                               return Err(("Fee spike buffer violation", 0x1000|7));
+                       }
+               }
+
+               Ok(())
+       }
+
        pub fn get_cur_holder_commitment_transaction_number(&self) -> u64 {
                self.context.cur_holder_commitment_transaction_number + 1
        }
@@ -6176,6 +6316,26 @@ impl<SP: Deref> Channel<SP> where
                }
        }
 
+       /// On startup, its possible we detect some monitor updates have actually completed (and the
+       /// ChannelManager was simply stale). In that case, we should simply drop them, which we do
+       /// here after logging them.
+       pub fn on_startup_drop_completed_blocked_mon_updates_through<L: Logger>(&mut self, logger: &L, loaded_mon_update_id: u64) {
+               let channel_id = self.context.channel_id();
+               self.context.blocked_monitor_updates.retain(|update| {
+                       if update.update.update_id <= loaded_mon_update_id {
+                               log_info!(
+                                       logger,
+                                       "Dropping completed ChannelMonitorUpdate id {} on channel {} due to a stale ChannelManager",
+                                       update.update.update_id,
+                                       channel_id,
+                               );
+                               false
+                       } else {
+                               true
+                       }
+               });
+       }
+
        pub fn blocked_monitor_updates_pending(&self) -> usize {
                self.context.blocked_monitor_updates.len()
        }
@@ -6343,8 +6503,8 @@ impl<SP: Deref> Channel<SP> where
                                if self.context.funding_tx_confirmation_height == 0 {
                                        if tx.txid() == funding_txo.txid {
                                                let txo_idx = funding_txo.index as usize;
-                                               if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.context.get_funding_redeemscript().to_v0_p2wsh() ||
-                                                               tx.output[txo_idx].value != self.context.channel_value_satoshis {
+                                               if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.context.get_funding_redeemscript().to_p2wsh() ||
+                                                               tx.output[txo_idx].value.to_sat() != self.context.channel_value_satoshis {
                                                        if self.context.is_outbound() {
                                                                // If we generated the funding transaction and it doesn't match what it
                                                                // should, the client is really broken and we should just panic and
@@ -6359,7 +6519,7 @@ impl<SP: Deref> Channel<SP> where
                                                        return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
                                                } else {
                                                        if self.context.is_outbound() {
-                                                               if !tx.is_coin_base() {
+                                                               if !tx.is_coinbase() {
                                                                        for input in tx.input.iter() {
                                                                                if input.witness.is_empty() {
                                                                                        // We generated a malleable funding transaction, implying we've
@@ -6379,7 +6539,7 @@ impl<SP: Deref> Channel<SP> where
                                                }
                                                // If this is a coinbase transaction and not a 0-conf channel
                                                // we should update our min_depth to 100 to handle coinbase maturity
-                                               if tx.is_coin_base() &&
+                                               if tx.is_coinbase() &&
                                                        self.context.minimum_depth.unwrap_or(0) > 0 &&
                                                        self.context.minimum_depth.unwrap_or(0) < COINBASE_MATURITY {
                                                        self.context.minimum_depth = Some(COINBASE_MATURITY);
@@ -7323,7 +7483,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
 
                // If the funding transaction is a coinbase transaction, we need to set the minimum depth to 100.
                // We can skip this if it is a zero-conf channel.
-               if funding_transaction.is_coin_base() &&
+               if funding_transaction.is_coinbase() &&
                        self.context.minimum_depth.unwrap_or(0) > 0 &&
                        self.context.minimum_depth.unwrap_or(0) < COINBASE_MATURITY {
                        self.context.minimum_depth = Some(COINBASE_MATURITY);
@@ -7361,6 +7521,12 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
                Ok(self.get_open_channel(chain_hash))
        }
 
+       /// Returns true if we can resume the channel by sending the [`msgs::OpenChannel`] again.
+       pub fn is_resumable(&self) -> bool {
+               !self.context.have_received_message() &&
+                       self.context.cur_holder_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER
+       }
+
        pub fn get_open_channel(&self, chain_hash: ChainHash) -> msgs::OpenChannel {
                if !self.context.is_outbound() {
                        panic!("Tried to open a channel for an inbound channel?");
@@ -7596,7 +7762,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
 
                let funding_redeemscript = self.context.get_funding_redeemscript();
                let funding_txo = self.context.get_funding_txo().unwrap();
-               let funding_txo_script = funding_redeemscript.to_v0_p2wsh();
+               let funding_txo_script = funding_redeemscript.to_p2wsh();
                let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.context.get_holder_pubkeys().payment_point, &self.context.get_counterparty_pubkeys().payment_point, self.context.is_outbound());
                let shutdown_script = self.context.shutdown_scriptpubkey.clone().map(|script| script.into_inner());
                let mut monitor_signer = signer_provider.derive_channel_signer(self.context.channel_value_satoshis, self.context.channel_keys_id);
@@ -7629,7 +7795,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
 
                let mut channel = Channel {
                        context: self.context,
-                       #[cfg(dual_funding)]
+                       #[cfg(any(dual_funding, splicing))]
                        dual_funding_channel_context: None,
                };
 
@@ -7703,7 +7869,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
                          F::Target: FeeEstimator,
                          L::Target: Logger,
        {
-               let logger = WithContext::from(logger, Some(counterparty_node_id), Some(msg.common_fields.temporary_channel_id));
+               let logger = WithContext::from(logger, Some(counterparty_node_id), Some(msg.common_fields.temporary_channel_id), None);
 
                // First check the channel type is known, failing before we do anything else if we don't
                // support this channel type.
@@ -7893,7 +8059,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
                let (counterparty_initial_commitment_tx, funding_signed) = self.context.get_funding_signed_msg(logger);
 
                let funding_redeemscript = self.context.get_funding_redeemscript();
-               let funding_txo_script = funding_redeemscript.to_v0_p2wsh();
+               let funding_txo_script = funding_redeemscript.to_p2wsh();
                let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.context.get_holder_pubkeys().payment_point, &self.context.get_counterparty_pubkeys().payment_point, self.context.is_outbound());
                let shutdown_script = self.context.shutdown_scriptpubkey.clone().map(|script| script.into_inner());
                let mut monitor_signer = signer_provider.derive_channel_signer(self.context.channel_value_satoshis, self.context.channel_keys_id);
@@ -7919,7 +8085,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
                // `ChannelMonitor`.
                let mut channel = Channel {
                        context: self.context,
-                       #[cfg(dual_funding)]
+                       #[cfg(any(dual_funding, splicing))]
                        dual_funding_channel_context: None,
                };
                let need_channel_ready = channel.check_get_channel_ready(0).is_some();
@@ -7930,15 +8096,15 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
 }
 
 // A not-yet-funded outbound (from holder) channel using V2 channel establishment.
-#[cfg(dual_funding)]
+#[cfg(any(dual_funding, splicing))]
 pub(super) struct OutboundV2Channel<SP: Deref> where SP::Target: SignerProvider {
        pub context: ChannelContext<SP>,
        pub unfunded_context: UnfundedChannelContext,
-       #[cfg(dual_funding)]
+       #[cfg(any(dual_funding, splicing))]
        pub dual_funding_context: DualFundingChannelContext,
 }
 
-#[cfg(dual_funding)]
+#[cfg(any(dual_funding, splicing))]
 impl<SP: Deref> OutboundV2Channel<SP> where SP::Target: SignerProvider {
        pub fn new<ES: Deref, F: Deref>(
                fee_estimator: &LowerBoundedFeeEstimator<F>, entropy_source: &ES, signer_provider: &SP,
@@ -8054,14 +8220,14 @@ impl<SP: Deref> OutboundV2Channel<SP> where SP::Target: SignerProvider {
 }
 
 // A not-yet-funded inbound (from counterparty) channel using V2 channel establishment.
-#[cfg(dual_funding)]
+#[cfg(any(dual_funding, splicing))]
 pub(super) struct InboundV2Channel<SP: Deref> where SP::Target: SignerProvider {
        pub context: ChannelContext<SP>,
        pub unfunded_context: UnfundedChannelContext,
        pub dual_funding_context: DualFundingChannelContext,
 }
 
-#[cfg(dual_funding)]
+#[cfg(any(dual_funding, splicing))]
 impl<SP: Deref> InboundV2Channel<SP> where SP::Target: SignerProvider {
        /// Creates a new dual-funded channel from a remote side's request for one.
        /// Assumes chain_hash has already been checked and corresponds with what we expect!
@@ -8232,7 +8398,7 @@ fn get_initial_channel_type(config: &UserConfig, their_features: &InitFeatures)
        ret
 }
 
-const SERIALIZATION_VERSION: u8 = 3;
+const SERIALIZATION_VERSION: u8 = 4;
 const MIN_SERIALIZATION_VERSION: u8 = 3;
 
 impl_writeable_tlv_based_enum!(InboundHTLCRemovalReason,;
@@ -8294,7 +8460,18 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
                // Note that we write out as if remove_uncommitted_htlcs_and_mark_paused had just been
                // called.
 
-               write_ver_prefix!(writer, MIN_SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
+               let version_to_write = if self.context.pending_inbound_htlcs.iter().any(|htlc| match htlc.state {
+                       InboundHTLCState::AwaitingRemoteRevokeToAnnounce(ref htlc_resolution)|
+                               InboundHTLCState::AwaitingAnnouncedRemoteRevoke(ref htlc_resolution) => {
+                               matches!(htlc_resolution, InboundHTLCResolution::Pending { .. })
+                       },
+                       _ => false,
+               }) {
+                       SERIALIZATION_VERSION
+               } else {
+                       MIN_SERIALIZATION_VERSION
+               };
+               write_ver_prefix!(writer, version_to_write, MIN_SERIALIZATION_VERSION);
 
                // `user_id` used to be a single u64 value. In order to remain backwards compatible with
                // versions prior to 0.0.113, the u128 is serialized as two separate u64 values. We write
@@ -8350,13 +8527,29 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
                        htlc.payment_hash.write(writer)?;
                        match &htlc.state {
                                &InboundHTLCState::RemoteAnnounced(_) => unreachable!(),
-                               &InboundHTLCState::AwaitingRemoteRevokeToAnnounce(ref htlc_state) => {
+                               &InboundHTLCState::AwaitingRemoteRevokeToAnnounce(ref htlc_resolution) => {
                                        1u8.write(writer)?;
-                                       htlc_state.write(writer)?;
+                                       if version_to_write <= 3 {
+                                               if let InboundHTLCResolution::Resolved { pending_htlc_status } = htlc_resolution {
+                                                       pending_htlc_status.write(writer)?;
+                                               } else {
+                                                       panic!();
+                                               }
+                                       } else {
+                                               htlc_resolution.write(writer)?;
+                                       }
                                },
-                               &InboundHTLCState::AwaitingAnnouncedRemoteRevoke(ref htlc_state) => {
+                               &InboundHTLCState::AwaitingAnnouncedRemoteRevoke(ref htlc_resolution) => {
                                        2u8.write(writer)?;
-                                       htlc_state.write(writer)?;
+                                       if version_to_write <= 3 {
+                                               if let InboundHTLCResolution::Resolved { pending_htlc_status } = htlc_resolution {
+                                                       pending_htlc_status.write(writer)?;
+                                               } else {
+                                                       panic!();
+                                               }
+                                       } else {
+                                               htlc_resolution.write(writer)?;
+                                       }
                                },
                                &InboundHTLCState::Committed => {
                                        3u8.write(writer)?;
@@ -8582,6 +8775,11 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
 
                let holder_max_accepted_htlcs = if self.context.holder_max_accepted_htlcs == DEFAULT_MAX_HTLCS { None } else { Some(self.context.holder_max_accepted_htlcs) };
 
+               let mut monitor_pending_update_adds = None;
+               if !self.context.monitor_pending_update_adds.is_empty() {
+                       monitor_pending_update_adds = Some(&self.context.monitor_pending_update_adds);
+               }
+
                write_tlv_fields!(writer, {
                        (0, self.context.announcement_sigs, option),
                        // minimum_depth and counterparty_selected_channel_reserve_satoshis used to have a
@@ -8599,6 +8797,7 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
                        (7, self.context.shutdown_scriptpubkey, option),
                        (8, self.context.blocked_monitor_updates, optional_vec),
                        (9, self.context.target_closing_feerate_sats_per_kw, option),
+                       (10, monitor_pending_update_adds, option), // Added in 0.0.122
                        (11, self.context.monitor_pending_finalized_fulfills, required_vec),
                        (13, self.context.channel_creation_height, required),
                        (15, preimages, required_vec),
@@ -8617,7 +8816,8 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
                        (39, pending_outbound_blinding_points, optional_vec),
                        (41, holding_cell_blinding_points, optional_vec),
                        (43, malformed_htlcs, optional_vec), // Added in 0.0.119
-                       (45, self.context.local_initiated_shutdown, option), // Added in 0.0.122
+                       // 45 and 47 are reserved for async signing
+                       (49, self.context.local_initiated_shutdown, option), // Added in 0.0.122
                });
 
                Ok(())
@@ -8693,8 +8893,22 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
                                cltv_expiry: Readable::read(reader)?,
                                payment_hash: Readable::read(reader)?,
                                state: match <u8 as Readable>::read(reader)? {
-                                       1 => InboundHTLCState::AwaitingRemoteRevokeToAnnounce(Readable::read(reader)?),
-                                       2 => InboundHTLCState::AwaitingAnnouncedRemoteRevoke(Readable::read(reader)?),
+                                       1 => {
+                                               let resolution = if ver <= 3 {
+                                                       InboundHTLCResolution::Resolved { pending_htlc_status: Readable::read(reader)? }
+                                               } else {
+                                                       Readable::read(reader)?
+                                               };
+                                               InboundHTLCState::AwaitingRemoteRevokeToAnnounce(resolution)
+                                       },
+                                       2 => {
+                                               let resolution = if ver <= 3 {
+                                                       InboundHTLCResolution::Resolved { pending_htlc_status: Readable::read(reader)? }
+                                               } else {
+                                                       Readable::read(reader)?
+                                               };
+                                               InboundHTLCState::AwaitingAnnouncedRemoteRevoke(resolution)
+                                       },
                                        3 => InboundHTLCState::Committed,
                                        4 => InboundHTLCState::LocalRemoved(Readable::read(reader)?),
                                        _ => return Err(DecodeError::InvalidValue),
@@ -8911,6 +9125,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
                let mut holding_cell_blinding_points_opt: Option<Vec<Option<PublicKey>>> = None;
 
                let mut malformed_htlcs: Option<Vec<(u64, u16, [u8; 32])>> = None;
+               let mut monitor_pending_update_adds: Option<Vec<msgs::UpdateAddHTLC>> = None;
 
                read_tlv_fields!(reader, {
                        (0, announcement_sigs, option),
@@ -8923,6 +9138,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
                        (7, shutdown_scriptpubkey, option),
                        (8, blocked_monitor_updates, optional_vec),
                        (9, target_closing_feerate_sats_per_kw, option),
+                       (10, monitor_pending_update_adds, option), // Added in 0.0.122
                        (11, monitor_pending_finalized_fulfills, optional_vec),
                        (13, channel_creation_height, option),
                        (15, preimages_opt, optional_vec),
@@ -8941,7 +9157,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
                        (39, pending_outbound_blinding_points_opt, optional_vec),
                        (41, holding_cell_blinding_points_opt, optional_vec),
                        (43, malformed_htlcs, optional_vec), // Added in 0.0.119
-                       (45, local_initiated_shutdown, option),
+                       // 45 and 47 are reserved for async signing
+                       (49, local_initiated_shutdown, option),
                });
 
                let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
@@ -9094,6 +9311,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
                                monitor_pending_forwards,
                                monitor_pending_failures,
                                monitor_pending_finalized_fulfills: monitor_pending_finalized_fulfills.unwrap(),
+                               monitor_pending_update_adds: monitor_pending_update_adds.unwrap_or(Vec::new()),
 
                                signer_pending_commitment_update: false,
                                signer_pending_funding: false,
@@ -9176,7 +9394,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
 
                                blocked_monitor_updates: blocked_monitor_updates.unwrap(),
                        },
-                       #[cfg(dual_funding)]
+                       #[cfg(any(dual_funding, splicing))]
                        dual_funding_channel_context: None,
                })
        }
@@ -9185,13 +9403,14 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
 #[cfg(test)]
 mod tests {
        use std::cmp;
+       use bitcoin::amount::Amount;
        use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::blockdata::script::{ScriptBuf, Builder};
-       use bitcoin::blockdata::transaction::{Transaction, TxOut};
+       use bitcoin::blockdata::transaction::{Transaction, TxOut, Version};
        use bitcoin::blockdata::opcodes;
-       use bitcoin::network::constants::Network;
+       use bitcoin::network::Network;
        use crate::ln::onion_utils::INVALID_ONION_BLINDING;
-       use crate::ln::{PaymentHash, PaymentPreimage};
+       use crate::ln::types::{PaymentHash, PaymentPreimage};
        use crate::ln::channel_keys::{RevocationKey, RevocationBasepoint};
        use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
        use crate::ln::channel::InitFeatures;
@@ -9218,9 +9437,8 @@ mod tests {
        use bitcoin::hashes::sha256::Hash as Sha256;
        use bitcoin::hashes::Hash;
        use bitcoin::hashes::hex::FromHex;
-       use bitcoin::hash_types::WPubkeyHash;
        use bitcoin::blockdata::locktime::absolute::LockTime;
-       use bitcoin::address::{WitnessProgram, WitnessVersion};
+       use bitcoin::{WitnessProgram, WitnessVersion, WPubkeyHash};
        use crate::prelude::*;
 
        #[test]
@@ -9376,8 +9594,8 @@ mod tests {
 
                // Node A --> Node B: funding created
                let output_script = node_a_chan.context.get_funding_redeemscript();
-               let tx = Transaction { version: 1, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
-                       value: 10000000, script_pubkey: output_script.clone(),
+               let tx = Transaction { version: Version::ONE, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
+                       value: Amount::from_sat(10000000), script_pubkey: output_script.clone(),
                }]};
                let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
                let funding_created_msg = node_a_chan.get_funding_created(tx.clone(), funding_outpoint, false, &&logger).map_err(|_| ()).unwrap();
@@ -9505,8 +9723,8 @@ mod tests {
 
                // Node A --> Node B: funding created
                let output_script = node_a_chan.context.get_funding_redeemscript();
-               let tx = Transaction { version: 1, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
-                       value: 10000000, script_pubkey: output_script.clone(),
+               let tx = Transaction { version: Version::ONE, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
+                       value: Amount::from_sat(10000000), script_pubkey: output_script.clone(),
                }]};
                let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
                let funding_created_msg = node_a_chan.get_funding_created(tx.clone(), funding_outpoint, false, &&logger).map_err(|_| ()).unwrap();
@@ -9694,8 +9912,8 @@ mod tests {
 
                // Node A --> Node B: funding created
                let output_script = node_a_chan.context.get_funding_redeemscript();
-               let tx = Transaction { version: 1, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
-                       value: 10000000, script_pubkey: output_script.clone(),
+               let tx = Transaction { version: Version::ONE, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
+                       value: Amount::from_sat(10000000), script_pubkey: output_script.clone(),
                }]};
                let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
                let funding_created_msg = node_a_chan.get_funding_created(tx.clone(), funding_outpoint, false, &&logger).map_err(|_| ()).unwrap();
@@ -9761,8 +9979,8 @@ mod tests {
                        &features, &outbound_chan.get_open_channel(ChainHash::using_genesis_block(network)), 7, &config, 0, &&logger, false
                ).unwrap();
                outbound_chan.accept_channel(&inbound_chan.get_accept_channel_message(), &config.channel_handshake_limits, &features).unwrap();
-               let tx = Transaction { version: 1, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
-                       value: 10000000, script_pubkey: outbound_chan.context.get_funding_redeemscript(),
+               let tx = Transaction { version: Version::ONE, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
+                       value: Amount::from_sat(10000000), script_pubkey: outbound_chan.context.get_funding_redeemscript(),
                }]};
                let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
                let funding_created = outbound_chan.get_funding_created(tx.clone(), funding_outpoint, false, &&logger).map_err(|_| ()).unwrap().unwrap();
@@ -10019,7 +10237,7 @@ mod tests {
                                                &htlc, $opt_anchors, &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
                                        let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, $opt_anchors, &keys);
                                        let htlc_sighashtype = if $opt_anchors.supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
-                                       let htlc_sighash = Message::from_slice(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype).unwrap()[..]).unwrap();
+                                       let htlc_sighash = Message::from_digest(sighash::SighashCache::new(&htlc_tx).p2wsh_signature_hash(0, &htlc_redeemscript, htlc.to_bitcoin_amount(), htlc_sighashtype).unwrap().as_raw_hash().to_byte_array());
                                        assert!(secp_ctx.verify_ecdsa(&htlc_sighash, &remote_signature, &keys.countersignatory_htlc_key.to_public_key()).is_ok(), "verify counterparty htlc sig");
 
                                        let mut preimage: Option<PaymentPreimage> = None;
@@ -10885,15 +11103,15 @@ mod tests {
                // Fund the channel with a batch funding transaction.
                let output_script = node_a_chan.context.get_funding_redeemscript();
                let tx = Transaction {
-                       version: 1,
+                       version: Version::ONE,
                        lock_time: LockTime::ZERO,
                        input: Vec::new(),
                        output: vec![
                                TxOut {
-                                       value: 10000000, script_pubkey: output_script.clone(),
+                                       value: Amount::from_sat(10000000), script_pubkey: output_script.clone(),
                                },
                                TxOut {
-                                       value: 10000000, script_pubkey: Builder::new().into_script(),
+                                       value: Amount::from_sat(10000000), script_pubkey: Builder::new().into_script(),
                                },
                        ]};
                let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
diff --git a/lightning/src/ln/channel_id.rs b/lightning/src/ln/channel_id.rs
deleted file mode 100644 (file)
index 90efe3c..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-// 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.
-
-//! ChannelId definition.
-
-use crate::chain::transaction::OutPoint;
-use crate::io;
-use crate::ln::msgs::DecodeError;
-use crate::sign::EntropySource;
-use crate::util::ser::{Readable, Writeable, Writer};
-use super::channel_keys::RevocationBasepoint;
-
-use bitcoin::hashes::{
-       Hash as _,
-       HashEngine as _,
-       sha256::Hash as Sha256,
-};
-use core::fmt;
-use core::ops::Deref;
-
-/// A unique 32-byte identifier for a channel.
-/// Depending on how the ID is generated, several varieties are distinguished
-/// (but all are stored as 32 bytes):
-///   _v1_ and _temporary_.
-/// A _v1_ channel ID is generated based on funding tx outpoint (txid & index).
-/// A _temporary_ ID is generated randomly.
-/// (Later revocation-point-based _v2_ is a possibility.)
-/// The variety (context) is not stored, it is relevant only at creation.
-///
-/// This is not exported to bindings users as we just use [u8; 32] directly.
-#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
-pub struct ChannelId(pub [u8; 32]);
-
-impl ChannelId {
-       /// Create _v1_ channel ID based on a funding TX ID and output index
-       pub fn v1_from_funding_txid(txid: &[u8; 32], output_index: u16) -> Self {
-               let mut res = [0; 32];
-               res[..].copy_from_slice(&txid[..]);
-               res[30] ^= ((output_index >> 8) & 0xff) as u8;
-               res[31] ^= ((output_index >> 0) & 0xff) as u8;
-               Self(res)
-       }
-
-       /// Create _v1_ channel ID from a funding tx outpoint
-       pub fn v1_from_funding_outpoint(outpoint: OutPoint) -> Self {
-               Self::v1_from_funding_txid(outpoint.txid.as_byte_array(), outpoint.index)
-       }
-
-       /// Create a _temporary_ channel ID randomly, based on an entropy source.
-       pub fn temporary_from_entropy_source<ES: Deref>(entropy_source: &ES) -> Self
-       where ES::Target: EntropySource {
-               Self(entropy_source.get_secure_random_bytes())
-       }
-
-       /// Generic constructor; create a new channel ID from the provided data.
-       /// Use a more specific `*_from_*` constructor when possible.
-       pub fn from_bytes(data: [u8; 32]) -> Self {
-               Self(data)
-       }
-
-       /// Create a channel ID consisting of all-zeros data (e.g. when uninitialized or a placeholder).
-       pub fn new_zero() -> Self {
-               Self([0; 32])
-       }
-
-       /// Check whether ID is consisting of all zeros (uninitialized)
-       pub fn is_zero(&self) -> bool {
-               self.0[..] == [0; 32]
-       }
-
-       /// Create _v2_ channel ID by concatenating the holder revocation basepoint with the counterparty
-       /// revocation basepoint and hashing the result. The basepoints will be concatenated in increasing
-       /// sorted order.
-       pub fn v2_from_revocation_basepoints(
-               ours: &RevocationBasepoint,
-               theirs: &RevocationBasepoint,
-       ) -> Self {
-               let ours = ours.0.serialize();
-               let theirs = theirs.0.serialize();
-               let (lesser, greater) = if ours < theirs {
-                       (ours, theirs)
-               } else {
-                       (theirs, ours)
-               };
-               let mut engine = Sha256::engine();
-               engine.input(&lesser[..]);
-               engine.input(&greater[..]);
-               Self(Sha256::from_engine(engine).to_byte_array())
-       }
-
-       /// Create temporary _v2_ channel ID by concatenating a zeroed out basepoint with the holder
-       /// revocation basepoint and hashing the result.
-       pub fn temporary_v2_from_revocation_basepoint(our_revocation_basepoint: &RevocationBasepoint) -> Self {
-               Self(Sha256::hash(&[[0u8; 33], our_revocation_basepoint.0.serialize()].concat()).to_byte_array())
-       }
-}
-
-impl Writeable for ChannelId {
-       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
-               self.0.write(w)
-       }
-}
-
-impl Readable for ChannelId {
-       fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
-               let buf: [u8; 32] = Readable::read(r)?;
-               Ok(ChannelId(buf))
-       }
-}
-
-impl fmt::Display for ChannelId {
-       fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-               crate::util::logger::DebugBytes(&self.0).fmt(f)
-       }
-}
-
-#[cfg(test)]
-mod tests {
-       use bitcoin::hashes::{
-               Hash as _,
-               HashEngine as _,
-               hex::FromHex as _,
-               sha256::Hash as Sha256,
-       };
-       use bitcoin::secp256k1::PublicKey;
-       use hex::DisplayHex;
-
-       use crate::ln::ChannelId;
-       use crate::ln::channel_keys::RevocationBasepoint;
-       use crate::util::ser::{Readable, Writeable};
-       use crate::util::test_utils;
-       use crate::prelude::*;
-       use crate::io;
-
-       #[test]
-       fn test_channel_id_v1_from_funding_txid() {
-               let channel_id = ChannelId::v1_from_funding_txid(&[2; 32], 1);
-               assert_eq!(channel_id.0.as_hex().to_string(), "0202020202020202020202020202020202020202020202020202020202020203");
-       }
-
-       #[test]
-       fn test_channel_id_new_from_data() {
-               let data: [u8; 32] = [2; 32];
-               let channel_id = ChannelId::from_bytes(data.clone());
-               assert_eq!(channel_id.0, data);
-       }
-
-       #[test]
-       fn test_channel_id_equals() {
-               let channel_id11 = ChannelId::v1_from_funding_txid(&[2; 32], 2);
-               let channel_id12 = ChannelId::v1_from_funding_txid(&[2; 32], 2);
-               let channel_id21 = ChannelId::v1_from_funding_txid(&[2; 32], 42);
-               assert_eq!(channel_id11, channel_id12);
-               assert_ne!(channel_id11, channel_id21);
-       }
-
-       #[test]
-       fn test_channel_id_write_read() {
-               let data: [u8; 32] = [2; 32];
-               let channel_id = ChannelId::from_bytes(data.clone());
-
-               let mut w = test_utils::TestVecWriter(Vec::new());
-               channel_id.write(&mut w).unwrap();
-
-               let channel_id_2 = ChannelId::read(&mut io::Cursor::new(&w.0)).unwrap();
-               assert_eq!(channel_id_2, channel_id);
-               assert_eq!(channel_id_2.0, data);
-       }
-
-       #[test]
-       fn test_channel_id_display() {
-               let channel_id = ChannelId::v1_from_funding_txid(&[2; 32], 1);
-               assert_eq!(format!("{}", &channel_id), "0202020202020202020202020202020202020202020202020202020202020203");
-       }
-
-       #[test]
-       fn test_channel_id_v2_from_basepoints() {
-               // Ours greater than theirs
-               let ours = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap());
-               let theirs = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap());
-
-               let mut engine = Sha256::engine();
-               engine.input(&theirs.0.serialize());
-               engine.input(&ours.0.serialize());
-               let expected_id = ChannelId(Sha256::from_engine(engine).to_byte_array());
-
-               assert_eq!(ChannelId::v2_from_revocation_basepoints(&ours, &theirs), expected_id);
-
-               // Theirs greater than ours
-               let ours = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap());
-               let theirs = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap());
-
-               let mut engine = Sha256::engine();
-               engine.input(&ours.0.serialize());
-               engine.input(&theirs.0.serialize());
-               let expected_id = ChannelId(Sha256::from_engine(engine).to_byte_array());
-
-               assert_eq!(ChannelId::v2_from_revocation_basepoints(&ours, &theirs), expected_id);
-       }
-}
index b577dc60008583537c4d9c1cebc8b2f6e5f48e77..9e839b15e3c40636c72553b32c9eb52f35079f7b 100644 (file)
 //! Keys used to generate commitment transactions.
 //! See: <https://github.com/lightning/bolts/blob/master/03-transactions.md#keys>
 
-use bitcoin::hashes::Hash;
-use bitcoin::hashes::HashEngine;
-use bitcoin::secp256k1::Scalar;
-use bitcoin::secp256k1::SecretKey;
-use bitcoin::secp256k1::Secp256k1;
-use bitcoin::secp256k1;
+use crate::io;
 use crate::ln::msgs::DecodeError;
 use crate::util::ser::Readable;
-use crate::io;
-use crate::util::ser::Writer;
 use crate::util::ser::Writeable;
-use bitcoin::secp256k1::PublicKey;
+use crate::util::ser::Writer;
 use bitcoin::hashes::sha256::Hash as Sha256;
+use bitcoin::hashes::Hash;
+use bitcoin::hashes::HashEngine;
+use bitcoin::secp256k1;
+use bitcoin::secp256k1::PublicKey;
+use bitcoin::secp256k1::Scalar;
+use bitcoin::secp256k1::Secp256k1;
+use bitcoin::secp256k1::SecretKey;
 
 macro_rules! doc_comment {
        ($x:expr, $($tt:tt)*) => {
@@ -31,12 +31,30 @@ macro_rules! doc_comment {
        };
 }
 macro_rules! basepoint_impl {
-       ($BasepointT:ty) => {
+       ($BasepointT:ty $(, $KeyName: expr)?) => {
                impl $BasepointT {
                        /// Get inner Public Key
                        pub fn to_public_key(&self) -> PublicKey {
                                self.0
                        }
+
+                       $(doc_comment!(
+                               concat!(
+                               "Derives the \"tweak\" used in calculate [`", $KeyName, "::from_basepoint`].\n",
+                               "\n",
+                               "[`", $KeyName, "::from_basepoint`] calculates a private key as:\n",
+                               "`privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)`\n",
+                               "\n",
+                               "This calculates the hash part in the tweak derivation process, which is used to\n",
+                               "ensure that each key is unique and cannot be guessed by an external party."
+                               ),
+                               pub fn derive_add_tweak(&self, per_commitment_point: &PublicKey) -> Sha256 {
+                                       let mut sha = Sha256::engine();
+                                       sha.input(&per_commitment_point.serialize());
+                                       sha.input(&self.to_public_key().serialize());
+                                       Sha256::from_engine(sha)
+                               });
+                       )?
                }
 
                impl From<PublicKey> for $BasepointT {
@@ -44,8 +62,7 @@ macro_rules! basepoint_impl {
                                Self(value)
                        }
                }
-
-       }
+       };
 }
 macro_rules! key_impl {
        ($BasepointT:ty, $KeyName:expr) => {
@@ -87,11 +104,9 @@ macro_rules! key_read_write {
                                Ok(Self(key))
                        }
                }
-       }
+       };
 }
 
-
-
 /// Base key used in conjunction with a `per_commitment_point` to generate a [`DelayedPaymentKey`].
 ///
 /// The delayed payment key is used to pay the commitment state broadcaster their
@@ -99,10 +114,9 @@ macro_rules! key_read_write {
 /// state broadcasted was previously revoked.
 #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
 pub struct DelayedPaymentBasepoint(pub PublicKey);
-basepoint_impl!(DelayedPaymentBasepoint);
+basepoint_impl!(DelayedPaymentBasepoint, "DelayedPaymentKey");
 key_read_write!(DelayedPaymentBasepoint);
 
-
 /// A derived key built from a [`DelayedPaymentBasepoint`] and `per_commitment_point`.
 ///
 /// The delayed payment key is used to pay the commitment state broadcaster their
@@ -127,7 +141,7 @@ key_read_write!(DelayedPaymentKey);
 /// Thus, both channel counterparties' HTLC keys will appears in each HTLC output's script.
 #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
 pub struct HtlcBasepoint(pub PublicKey);
-basepoint_impl!(HtlcBasepoint);
+basepoint_impl!(HtlcBasepoint, "HtlcKey");
 key_read_write!(HtlcBasepoint);
 
 /// A derived key built from a [`HtlcBasepoint`] and `per_commitment_point`.
@@ -150,14 +164,28 @@ key_read_write!(HtlcKey);
 /// Derives a per-commitment-transaction public key (eg an htlc key or a delayed_payment key)
 /// from the base point and the per_commitment_key. This is the public equivalent of
 /// derive_private_key - using only public keys to derive a public key instead of private keys.
-fn derive_public_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_point: &PublicKey) -> PublicKey {
+fn derive_public_key<T: secp256k1::Signing>(
+       secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_point: &PublicKey,
+) -> PublicKey {
        let mut sha = Sha256::engine();
        sha.input(&per_commitment_point.serialize());
        sha.input(&base_point.serialize());
-       let res = Sha256::from_engine(sha).to_byte_array();
+       let res = Sha256::from_engine(sha);
 
-       let hashkey = PublicKey::from_secret_key(&secp_ctx,
-               &SecretKey::from_slice(&res).expect("Hashes should always be valid keys unless SHA-256 is broken"));
+       add_public_key_tweak(secp_ctx, base_point, &res)
+}
+
+/// Adds a tweak to a public key to derive a new public key.
+///
+/// May panic if `tweak` is not the output of a SHA-256 hash.
+pub fn add_public_key_tweak<T: secp256k1::Signing>(
+       secp_ctx: &Secp256k1<T>, base_point: &PublicKey, tweak: &Sha256,
+) -> PublicKey {
+       let hashkey = PublicKey::from_secret_key(
+               &secp_ctx,
+               &SecretKey::from_slice(tweak.as_byte_array())
+                       .expect("Hashes should always be valid keys unless SHA-256 is broken"),
+       );
        base_point.combine(&hashkey)
                .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak contains the hash of the key.")
 }
@@ -169,7 +197,6 @@ pub struct RevocationBasepoint(pub PublicKey);
 basepoint_impl!(RevocationBasepoint);
 key_read_write!(RevocationBasepoint);
 
-
 /// The revocation key is used to allow a channel party to revoke their state - giving their
 /// counterparty the required material to claim all of their funds if they broadcast that state.
 ///
@@ -192,8 +219,7 @@ impl RevocationKey {
        ///
        /// [`chan_utils::derive_private_revocation_key`]: crate::ln::chan_utils::derive_private_revocation_key
        pub fn from_basepoint<T: secp256k1::Verification>(
-               secp_ctx: &Secp256k1<T>,
-               countersignatory_basepoint: &RevocationBasepoint,
+               secp_ctx: &Secp256k1<T>, countersignatory_basepoint: &RevocationBasepoint,
                per_commitment_point: &PublicKey,
        ) -> Self {
                let rev_append_commit_hash_key = {
@@ -227,28 +253,56 @@ impl RevocationKey {
 }
 key_read_write!(RevocationKey);
 
-
 #[cfg(test)]
 mod test {
-       use bitcoin::secp256k1::{Secp256k1, SecretKey, PublicKey};
-       use bitcoin::hashes::hex::FromHex;
        use super::derive_public_key;
+       use bitcoin::hashes::hex::FromHex;
+       use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
 
        #[test]
        fn test_key_derivation() {
                // Test vectors from BOLT 3 Appendix E:
                let secp_ctx = Secp256k1::new();
 
-               let base_secret = SecretKey::from_slice(&<Vec<u8>>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap()[..]).unwrap();
-               let per_commitment_secret = SecretKey::from_slice(&<Vec<u8>>::from_hex("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap();
+               let base_secret = SecretKey::from_slice(
+                       &<Vec<u8>>::from_hex(
+                               "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+                       )
+                       .unwrap()[..],
+               )
+               .unwrap();
+               let per_commitment_secret = SecretKey::from_slice(
+                       &<Vec<u8>>::from_hex(
+                               "1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100",
+                       )
+                       .unwrap()[..],
+               )
+               .unwrap();
 
                let base_point = PublicKey::from_secret_key(&secp_ctx, &base_secret);
-               assert_eq!(base_point.serialize()[..], <Vec<u8>>::from_hex("036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2").unwrap()[..]);
+               assert_eq!(
+                       base_point.serialize()[..],
+                       <Vec<u8>>::from_hex(
+                               "036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2"
+                       )
+                       .unwrap()[..]
+               );
 
                let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
-               assert_eq!(per_commitment_point.serialize()[..], <Vec<u8>>::from_hex("025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486").unwrap()[..]);
-
-               assert_eq!(derive_public_key(&secp_ctx, &per_commitment_point, &base_point).serialize()[..],
-                       <Vec<u8>>::from_hex("0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5").unwrap()[..]);
+               assert_eq!(
+                       per_commitment_point.serialize()[..],
+                       <Vec<u8>>::from_hex(
+                               "025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486"
+                       )
+                       .unwrap()[..]
+               );
+
+               assert_eq!(
+                       derive_public_key(&secp_ctx, &per_commitment_point, &base_point).serialize()[..],
+                       <Vec<u8>>::from_hex(
+                               "0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5"
+                       )
+                       .unwrap()[..]
+               );
        }
 }
index 6426f0925f31f7bb1aafda8c827354fed67b1ad6..7bfeeac6def5cef5144c5a0fe536073ac2bd7c45 100644 (file)
@@ -21,7 +21,7 @@ use bitcoin::blockdata::block::Header;
 use bitcoin::blockdata::transaction::Transaction;
 use bitcoin::blockdata::constants::ChainHash;
 use bitcoin::key::constants::SECRET_KEY_SIZE;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 
 use bitcoin::hashes::Hash;
 use bitcoin::hashes::sha256::Hash as Sha256;
@@ -31,8 +31,9 @@ use bitcoin::secp256k1::{SecretKey,PublicKey};
 use bitcoin::secp256k1::Secp256k1;
 use bitcoin::{secp256k1, Sequence};
 
-use crate::blinded_path::BlindedPath;
-use crate::blinded_path::payment::{PaymentConstraints, ReceiveTlvs};
+use crate::blinded_path::{BlindedPath, NodeIdLookUp};
+use crate::blinded_path::message::ForwardNode;
+use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentConstraints, PaymentContext, ReceiveTlvs};
 use crate::chain;
 use crate::chain::{Confirm, ChannelMonitorUpdateStatus, Watch, BestBlock};
 use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator};
@@ -42,7 +43,8 @@ use crate::events;
 use crate::events::{Event, EventHandler, EventsProvider, MessageSendEvent, MessageSendEventsProvider, ClosureReason, HTLCDestination, PaymentFailureReason};
 // Since this struct is returned in `list_channels` methods, expose it here in case users want to
 // construct one themselves.
-use crate::ln::{inbound_payment, ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
+use crate::ln::inbound_payment;
+use crate::ln::types::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
 use crate::ln::channel::{self, Channel, ChannelPhase, ChannelContext, ChannelError, ChannelUpdateStatus, ShutdownResult, UnfundedChannelContext, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel, WithChannelContext};
 pub use crate::ln::channel::{InboundHTLCDetails, InboundHTLCStateDetails, OutboundHTLCDetails, OutboundHTLCStateDetails};
 use crate::ln::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
@@ -61,14 +63,13 @@ use crate::ln::wire::Encode;
 use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
 use crate::offers::invoice_error::InvoiceError;
 use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequestBuilder};
-use crate::offers::merkle::SignError;
 use crate::offers::offer::{Offer, OfferBuilder};
 use crate::offers::parse::Bolt12SemanticError;
 use crate::offers::refund::{Refund, RefundBuilder};
-use crate::onion_message::messenger::{Destination, MessageRouter, PendingOnionMessage, new_pending_onion_message};
+use crate::onion_message::messenger::{new_pending_onion_message, Destination, MessageRouter, PendingOnionMessage, Responder, ResponseInstruction};
 use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
 use crate::sign::{EntropySource, NodeSigner, Recipient, SignerProvider};
-use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
+use crate::sign::ecdsa::EcdsaChannelSigner;
 use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
 use crate::util::wakers::{Future, Notifier};
 use crate::util::scid_utils::fake_scid;
@@ -76,6 +77,7 @@ use crate::util::string::UntrustedString;
 use crate::util::ser::{BigSize, FixedLengthReader, Readable, ReadableArgs, MaybeReadable, Writeable, Writer, VecWriter};
 use crate::util::logger::{Level, Logger, WithContext};
 use crate::util::errors::APIError;
+
 #[cfg(not(c_bindings))]
 use {
        crate::offers::offer::DerivedMetadata,
@@ -156,6 +158,11 @@ pub enum PendingHTLCRouting {
                /// [`Event::PaymentClaimable::onion_fields`] as
                /// [`RecipientOnionFields::payment_metadata`].
                payment_metadata: Option<Vec<u8>>,
+               /// The context of the payment included by the recipient in a blinded path, or `None` if a
+               /// blinded path was not used.
+               ///
+               /// Used in part to determine the [`events::PaymentPurpose`].
+               payment_context: Option<PaymentContext>,
                /// CLTV expiry of the received HTLC.
                ///
                /// Used to track when we should expire pending HTLCs that go unclaimed.
@@ -200,6 +207,8 @@ pub enum PendingHTLCRouting {
                /// For HTLCs received by LDK, these will ultimately bubble back up as
                /// [`RecipientOnionFields::custom_tlvs`].
                custom_tlvs: Vec<(u64, Vec<u8>)>,
+               /// Set if this HTLC is the final hop in a multi-hop blinded path.
+               requires_blinded_error: bool,
        },
 }
 
@@ -221,6 +230,7 @@ impl PendingHTLCRouting {
                match self {
                        Self::Forward { blinded: Some(BlindedForward { failure, .. }), .. } => Some(*failure),
                        Self::Receive { requires_blinded_error: true, .. } => Some(BlindedFailure::FromBlindedNode),
+                       Self::ReceiveKeysend { requires_blinded_error: true, .. } => Some(BlindedFailure::FromBlindedNode),
                        _ => None,
                }
        }
@@ -671,6 +681,7 @@ struct ClaimingPayment {
        receiver_node_id: PublicKey,
        htlcs: Vec<events::ClaimedHTLC>,
        sender_intended_value: Option<u64>,
+       onion_fields: Option<RecipientOnionFields>,
 }
 impl_writeable_tlv_based!(ClaimingPayment, {
        (0, amount_msat, required),
@@ -678,6 +689,7 @@ impl_writeable_tlv_based!(ClaimingPayment, {
        (4, receiver_node_id, required),
        (5, htlcs, optional_vec),
        (7, sender_intended_value, option),
+       (9, onion_fields, option),
 });
 
 struct ClaimablePayment {
@@ -900,7 +912,7 @@ pub(super) struct PeerState<SP: Deref> where SP::Target: SignerProvider {
        /// The peer is currently connected (i.e. we've seen a
        /// [`ChannelMessageHandler::peer_connected`] and no corresponding
        /// [`ChannelMessageHandler::peer_disconnected`].
-       is_connected: bool,
+       pub is_connected: bool,
 }
 
 impl <SP: Deref> PeerState<SP> where SP::Target: SignerProvider {
@@ -915,9 +927,9 @@ impl <SP: Deref> PeerState<SP> where SP::Target: SignerProvider {
                        match phase {
                                ChannelPhase::Funded(_) | ChannelPhase::UnfundedOutboundV1(_) => true,
                                ChannelPhase::UnfundedInboundV1(_) => false,
-                               #[cfg(dual_funding)]
+                               #[cfg(any(dual_funding, splicing))]
                                ChannelPhase::UnfundedOutboundV2(_) => true,
-                               #[cfg(dual_funding)]
+                               #[cfg(any(dual_funding, splicing))]
                                ChannelPhase::UnfundedInboundV2(_) => false,
                        }
                )
@@ -1052,8 +1064,8 @@ pub trait AChannelManager {
        type NodeSigner: NodeSigner + ?Sized;
        /// A type that may be dereferenced to [`Self::NodeSigner`].
        type NS: Deref<Target = Self::NodeSigner>;
-       /// A type implementing [`WriteableEcdsaChannelSigner`].
-       type Signer: WriteableEcdsaChannelSigner + Sized;
+       /// A type implementing [`EcdsaChannelSigner`].
+       type Signer: EcdsaChannelSigner + Sized;
        /// A type implementing [`SignerProvider`] for [`Self::Signer`].
        type SignerProvider: SignerProvider<EcdsaSigner= Self::Signer> + ?Sized;
        /// A type that may be dereferenced to [`Self::SignerProvider`].
@@ -1106,11 +1118,629 @@ where
        fn get_cm(&self) -> &ChannelManager<M, T, ES, NS, SP, F, R, L> { self }
 }
 
-/// Manager which keeps track of a number of channels and sends messages to the appropriate
-/// channel, also tracking HTLC preimages and forwarding onion packets appropriately.
+/// A lightning node's channel state machine and payment management logic, which facilitates
+/// sending, forwarding, and receiving payments through lightning channels.
+///
+/// [`ChannelManager`] is parameterized by a number of components to achieve this.
+/// - [`chain::Watch`] (typically [`ChainMonitor`]) for on-chain monitoring and enforcement of each
+///   channel
+/// - [`BroadcasterInterface`] for broadcasting transactions related to opening, funding, and
+///   closing channels
+/// - [`EntropySource`] for providing random data needed for cryptographic operations
+/// - [`NodeSigner`] for cryptographic operations scoped to the node
+/// - [`SignerProvider`] for providing signers whose operations are scoped to individual channels
+/// - [`FeeEstimator`] to determine transaction fee rates needed to have a transaction mined in a
+///   timely manner
+/// - [`Router`] for finding payment paths when initiating and retrying payments
+/// - [`Logger`] for logging operational information of varying degrees
+///
+/// Additionally, it implements the following traits:
+/// - [`ChannelMessageHandler`] to handle off-chain channel activity from peers
+/// - [`MessageSendEventsProvider`] to similarly send such messages to peers
+/// - [`OffersMessageHandler`] for BOLT 12 message handling and sending
+/// - [`EventsProvider`] to generate user-actionable [`Event`]s
+/// - [`chain::Listen`] and [`chain::Confirm`] for notification of on-chain activity
+///
+/// Thus, [`ChannelManager`] is typically used to parameterize a [`MessageHandler`] and an
+/// [`OnionMessenger`]. The latter is required to support BOLT 12 functionality.
+///
+/// # `ChannelManager` vs `ChannelMonitor`
+///
+/// It's important to distinguish between the *off-chain* management and *on-chain* enforcement of
+/// lightning channels. [`ChannelManager`] exchanges messages with peers to manage the off-chain
+/// state of each channel. During this process, it generates a [`ChannelMonitor`] for each channel
+/// and a [`ChannelMonitorUpdate`] for each relevant change, notifying its parameterized
+/// [`chain::Watch`] of them.
+///
+/// An implementation of [`chain::Watch`], such as [`ChainMonitor`], is responsible for aggregating
+/// these [`ChannelMonitor`]s and applying any [`ChannelMonitorUpdate`]s to them. It then monitors
+/// for any pertinent on-chain activity, enforcing claims as needed.
+///
+/// This division of off-chain management and on-chain enforcement allows for interesting node
+/// setups. For instance, on-chain enforcement could be moved to a separate host or have added
+/// redundancy, possibly as a watchtower. See [`chain::Watch`] for the relevant interface.
+///
+/// # Initialization
+///
+/// Use [`ChannelManager::new`] with the most recent [`BlockHash`] when creating a fresh instance.
+/// Otherwise, if restarting, construct [`ChannelManagerReadArgs`] with the necessary parameters and
+/// references to any deserialized [`ChannelMonitor`]s that were previously persisted. Use this to
+/// deserialize the [`ChannelManager`] and feed it any new chain data since it was last online, as
+/// detailed in the [`ChannelManagerReadArgs`] documentation.
+///
+/// ```
+/// use bitcoin::BlockHash;
+/// use bitcoin::network::Network;
+/// use lightning::chain::BestBlock;
+/// # use lightning::chain::channelmonitor::ChannelMonitor;
+/// use lightning::ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs};
+/// # use lightning::routing::gossip::NetworkGraph;
+/// use lightning::util::config::UserConfig;
+/// use lightning::util::ser::ReadableArgs;
+///
+/// # fn read_channel_monitors() -> Vec<ChannelMonitor<lightning::sign::InMemorySigner>> { vec![] }
+/// # fn example<
+/// #     'a,
+/// #     L: lightning::util::logger::Logger,
+/// #     ES: lightning::sign::EntropySource,
+/// #     S: for <'b> lightning::routing::scoring::LockableScore<'b, ScoreLookUp = SL>,
+/// #     SL: lightning::routing::scoring::ScoreLookUp<ScoreParams = SP>,
+/// #     SP: Sized,
+/// #     R: lightning::io::Read,
+/// # >(
+/// #     fee_estimator: &dyn lightning::chain::chaininterface::FeeEstimator,
+/// #     chain_monitor: &dyn lightning::chain::Watch<lightning::sign::InMemorySigner>,
+/// #     tx_broadcaster: &dyn lightning::chain::chaininterface::BroadcasterInterface,
+/// #     router: &lightning::routing::router::DefaultRouter<&NetworkGraph<&'a L>, &'a L, &ES, &S, SP, SL>,
+/// #     logger: &L,
+/// #     entropy_source: &ES,
+/// #     node_signer: &dyn lightning::sign::NodeSigner,
+/// #     signer_provider: &lightning::sign::DynSignerProvider,
+/// #     best_block: lightning::chain::BestBlock,
+/// #     current_timestamp: u32,
+/// #     mut reader: R,
+/// # ) -> Result<(), lightning::ln::msgs::DecodeError> {
+/// // Fresh start with no channels
+/// let params = ChainParameters {
+///     network: Network::Bitcoin,
+///     best_block,
+/// };
+/// let default_config = UserConfig::default();
+/// let channel_manager = ChannelManager::new(
+///     fee_estimator, chain_monitor, tx_broadcaster, router, logger, entropy_source, node_signer,
+///     signer_provider, default_config, params, current_timestamp
+/// );
+///
+/// // Restart from deserialized data
+/// let mut channel_monitors = read_channel_monitors();
+/// let args = ChannelManagerReadArgs::new(
+///     entropy_source, node_signer, signer_provider, fee_estimator, chain_monitor, tx_broadcaster,
+///     router, logger, default_config, channel_monitors.iter_mut().collect()
+/// );
+/// let (block_hash, channel_manager) =
+///     <(BlockHash, ChannelManager<_, _, _, _, _, _, _, _>)>::read(&mut reader, args)?;
+///
+/// // Update the ChannelManager and ChannelMonitors with the latest chain data
+/// // ...
+///
+/// // Move the monitors to the ChannelManager's chain::Watch parameter
+/// for monitor in channel_monitors {
+///     chain_monitor.watch_channel(monitor.get_funding_txo().0, monitor);
+/// }
+/// # Ok(())
+/// # }
+/// ```
+///
+/// # Operation
+///
+/// The following is required for [`ChannelManager`] to function properly:
+/// - Handle messages from peers using its [`ChannelMessageHandler`] implementation (typically
+///   called by [`PeerManager::read_event`] when processing network I/O)
+/// - Send messages to peers obtained via its [`MessageSendEventsProvider`] implementation
+///   (typically initiated when [`PeerManager::process_events`] is called)
+/// - Feed on-chain activity using either its [`chain::Listen`] or [`chain::Confirm`] implementation
+///   as documented by those traits
+/// - Perform any periodic channel and payment checks by calling [`timer_tick_occurred`] roughly
+///   every minute
+/// - Persist to disk whenever [`get_and_clear_needs_persistence`] returns `true` using a
+///   [`Persister`] such as a [`KVStore`] implementation
+/// - Handle [`Event`]s obtained via its [`EventsProvider`] implementation
+///
+/// The [`Future`] returned by [`get_event_or_persistence_needed_future`] is useful in determining
+/// when the last two requirements need to be checked.
+///
+/// The [`lightning-block-sync`] and [`lightning-transaction-sync`] crates provide utilities that
+/// simplify feeding in on-chain activity using the [`chain::Listen`] and [`chain::Confirm`] traits,
+/// respectively. The remaining requirements can be met using the [`lightning-background-processor`]
+/// crate. For languages other than Rust, the availability of similar utilities may vary.
+///
+/// # Channels
+///
+/// [`ChannelManager`]'s primary function involves managing a channel state. Without channels,
+/// payments can't be sent. Use [`list_channels`] or [`list_usable_channels`] for a snapshot of the
+/// currently open channels.
+///
+/// ```
+/// # use lightning::ln::channelmanager::AChannelManager;
+/// #
+/// # fn example<T: AChannelManager>(channel_manager: T) {
+/// # let channel_manager = channel_manager.get_cm();
+/// let channels = channel_manager.list_usable_channels();
+/// for details in channels {
+///     println!("{:?}", details);
+/// }
+/// # }
+/// ```
+///
+/// Each channel is identified using a [`ChannelId`], which will change throughout the channel's
+/// life cycle. Additionally, channels are assigned a `user_channel_id`, which is given in
+/// [`Event`]s associated with the channel and serves as a fixed identifier but is otherwise unused
+/// by [`ChannelManager`].
+///
+/// ## Opening Channels
+///
+/// To an open a channel with a peer, call [`create_channel`]. This will initiate the process of
+/// opening an outbound channel, which requires self-funding when handling
+/// [`Event::FundingGenerationReady`].
+///
+/// ```
+/// # use bitcoin::{ScriptBuf, Transaction};
+/// # use bitcoin::secp256k1::PublicKey;
+/// # use lightning::ln::channelmanager::AChannelManager;
+/// # use lightning::events::{Event, EventsProvider};
+/// #
+/// # trait Wallet {
+/// #     fn create_funding_transaction(
+/// #         &self, _amount_sats: u64, _output_script: ScriptBuf
+/// #     ) -> Transaction;
+/// # }
+/// #
+/// # fn example<T: AChannelManager, W: Wallet>(channel_manager: T, wallet: W, peer_id: PublicKey) {
+/// # let channel_manager = channel_manager.get_cm();
+/// let value_sats = 1_000_000;
+/// let push_msats = 10_000_000;
+/// match channel_manager.create_channel(peer_id, value_sats, push_msats, 42, None, None) {
+///     Ok(channel_id) => println!("Opening channel {}", channel_id),
+///     Err(e) => println!("Error opening channel: {:?}", e),
+/// }
+///
+/// // On the event processing thread once the peer has responded
+/// channel_manager.process_pending_events(&|event| match event {
+///     Event::FundingGenerationReady {
+///         temporary_channel_id, counterparty_node_id, channel_value_satoshis, output_script,
+///         user_channel_id, ..
+///     } => {
+///         assert_eq!(user_channel_id, 42);
+///         let funding_transaction = wallet.create_funding_transaction(
+///             channel_value_satoshis, output_script
+///         );
+///         match channel_manager.funding_transaction_generated(
+///             &temporary_channel_id, &counterparty_node_id, funding_transaction
+///         ) {
+///             Ok(()) => println!("Funding channel {}", temporary_channel_id),
+///             Err(e) => println!("Error funding channel {}: {:?}", temporary_channel_id, e),
+///         }
+///     },
+///     Event::ChannelPending { channel_id, user_channel_id, former_temporary_channel_id, .. } => {
+///         assert_eq!(user_channel_id, 42);
+///         println!(
+///             "Channel {} now {} pending (funding transaction has been broadcasted)", channel_id,
+///             former_temporary_channel_id.unwrap()
+///         );
+///     },
+///     Event::ChannelReady { channel_id, user_channel_id, .. } => {
+///         assert_eq!(user_channel_id, 42);
+///         println!("Channel {} ready", channel_id);
+///     },
+///     // ...
+/// #     _ => {},
+/// });
+/// # }
+/// ```
+///
+/// ## Accepting Channels
+///
+/// Inbound channels are initiated by peers and are automatically accepted unless [`ChannelManager`]
+/// has [`UserConfig::manually_accept_inbound_channels`] set. In that case, the channel may be
+/// either accepted or rejected when handling [`Event::OpenChannelRequest`].
+///
+/// ```
+/// # use bitcoin::secp256k1::PublicKey;
+/// # use lightning::ln::channelmanager::AChannelManager;
+/// # use lightning::events::{Event, EventsProvider};
+/// #
+/// # fn is_trusted(counterparty_node_id: PublicKey) -> bool {
+/// #     // ...
+/// #     unimplemented!()
+/// # }
+/// #
+/// # fn example<T: AChannelManager>(channel_manager: T) {
+/// # let channel_manager = channel_manager.get_cm();
+/// channel_manager.process_pending_events(&|event| match event {
+///     Event::OpenChannelRequest { temporary_channel_id, counterparty_node_id, ..  } => {
+///         if !is_trusted(counterparty_node_id) {
+///             match channel_manager.force_close_without_broadcasting_txn(
+///                 &temporary_channel_id, &counterparty_node_id
+///             ) {
+///                 Ok(()) => println!("Rejecting channel {}", temporary_channel_id),
+///                 Err(e) => println!("Error rejecting channel {}: {:?}", temporary_channel_id, e),
+///             }
+///             return;
+///         }
+///
+///         let user_channel_id = 43;
+///         match channel_manager.accept_inbound_channel(
+///             &temporary_channel_id, &counterparty_node_id, user_channel_id
+///         ) {
+///             Ok(()) => println!("Accepting channel {}", temporary_channel_id),
+///             Err(e) => println!("Error accepting channel {}: {:?}", temporary_channel_id, e),
+///         }
+///     },
+///     // ...
+/// #     _ => {},
+/// });
+/// # }
+/// ```
+///
+/// ## Closing Channels
+///
+/// There are two ways to close a channel: either cooperatively using [`close_channel`] or
+/// unilaterally using [`force_close_broadcasting_latest_txn`]. The former is ideal as it makes for
+/// lower fees and immediate access to funds. However, the latter may be necessary if the
+/// counterparty isn't behaving properly or has gone offline. [`Event::ChannelClosed`] is generated
+/// once the channel has been closed successfully.
+///
+/// ```
+/// # use bitcoin::secp256k1::PublicKey;
+/// # use lightning::ln::types::ChannelId;
+/// # use lightning::ln::channelmanager::AChannelManager;
+/// # use lightning::events::{Event, EventsProvider};
+/// #
+/// # fn example<T: AChannelManager>(
+/// #     channel_manager: T, channel_id: ChannelId, counterparty_node_id: PublicKey
+/// # ) {
+/// # let channel_manager = channel_manager.get_cm();
+/// match channel_manager.close_channel(&channel_id, &counterparty_node_id) {
+///     Ok(()) => println!("Closing channel {}", channel_id),
+///     Err(e) => println!("Error closing channel {}: {:?}", channel_id, e),
+/// }
+///
+/// // On the event processing thread
+/// channel_manager.process_pending_events(&|event| match event {
+///     Event::ChannelClosed { channel_id, user_channel_id, ..  } => {
+///         assert_eq!(user_channel_id, 42);
+///         println!("Channel {} closed", channel_id);
+///     },
+///     // ...
+/// #     _ => {},
+/// });
+/// # }
+/// ```
+///
+/// # Payments
+///
+/// [`ChannelManager`] is responsible for sending, forwarding, and receiving payments through its
+/// channels. A payment is typically initiated from a [BOLT 11] invoice or a [BOLT 12] offer, though
+/// spontaneous (i.e., keysend) payments are also possible. Incoming payments don't require
+/// maintaining any additional state as [`ChannelManager`] can reconstruct the [`PaymentPreimage`]
+/// from the [`PaymentSecret`]. Sending payments, however, require tracking in order to retry failed
+/// HTLCs.
+///
+/// After a payment is initiated, it will appear in [`list_recent_payments`] until a short time
+/// after either an [`Event::PaymentSent`] or [`Event::PaymentFailed`] is handled. Failed HTLCs
+/// for a payment will be retried according to the payment's [`Retry`] strategy or until
+/// [`abandon_payment`] is called.
+///
+/// ## BOLT 11 Invoices
+///
+/// The [`lightning-invoice`] crate is useful for creating BOLT 11 invoices. Specifically, use the
+/// functions in its `utils` module for constructing invoices that are compatible with
+/// [`ChannelManager`]. These functions serve as a convenience for building invoices with the
+/// [`PaymentHash`] and [`PaymentSecret`] returned from [`create_inbound_payment`]. To provide your
+/// own [`PaymentHash`], use [`create_inbound_payment_for_hash`] or the corresponding functions in
+/// the [`lightning-invoice`] `utils` module.
+///
+/// [`ChannelManager`] generates an [`Event::PaymentClaimable`] once the full payment has been
+/// received. Call [`claim_funds`] to release the [`PaymentPreimage`], which in turn will result in
+/// an [`Event::PaymentClaimed`].
+///
+/// ```
+/// # use lightning::events::{Event, EventsProvider, PaymentPurpose};
+/// # use lightning::ln::channelmanager::AChannelManager;
+/// #
+/// # fn example<T: AChannelManager>(channel_manager: T) {
+/// # let channel_manager = channel_manager.get_cm();
+/// // Or use utils::create_invoice_from_channelmanager
+/// let known_payment_hash = match channel_manager.create_inbound_payment(
+///     Some(10_000_000), 3600, None
+/// ) {
+///     Ok((payment_hash, _payment_secret)) => {
+///         println!("Creating inbound payment {}", payment_hash);
+///         payment_hash
+///     },
+///     Err(()) => panic!("Error creating inbound payment"),
+/// };
+///
+/// // On the event processing thread
+/// channel_manager.process_pending_events(&|event| match event {
+///     Event::PaymentClaimable { payment_hash, purpose, .. } => match purpose {
+///         PaymentPurpose::Bolt11InvoicePayment { payment_preimage: Some(payment_preimage), .. } => {
+///             assert_eq!(payment_hash, known_payment_hash);
+///             println!("Claiming payment {}", payment_hash);
+///             channel_manager.claim_funds(payment_preimage);
+///         },
+///         PaymentPurpose::Bolt11InvoicePayment { payment_preimage: None, .. } => {
+///             println!("Unknown payment hash: {}", payment_hash);
+///         },
+///         PaymentPurpose::SpontaneousPayment(payment_preimage) => {
+///             assert_ne!(payment_hash, known_payment_hash);
+///             println!("Claiming spontaneous payment {}", payment_hash);
+///             channel_manager.claim_funds(payment_preimage);
+///         },
+///         // ...
+/// #         _ => {},
+///     },
+///     Event::PaymentClaimed { payment_hash, amount_msat, .. } => {
+///         assert_eq!(payment_hash, known_payment_hash);
+///         println!("Claimed {} msats", amount_msat);
+///     },
+///     // ...
+/// #     _ => {},
+/// });
+/// # }
+/// ```
+///
+/// For paying an invoice, [`lightning-invoice`] provides a `payment` module with convenience
+/// functions for use with [`send_payment`].
+///
+/// ```
+/// # use lightning::events::{Event, EventsProvider};
+/// # use lightning::ln::types::PaymentHash;
+/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, RecipientOnionFields, Retry};
+/// # use lightning::routing::router::RouteParameters;
+/// #
+/// # fn example<T: AChannelManager>(
+/// #     channel_manager: T, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
+/// #     route_params: RouteParameters, retry: Retry
+/// # ) {
+/// # let channel_manager = channel_manager.get_cm();
+/// // let (payment_hash, recipient_onion, route_params) =
+/// //     payment::payment_parameters_from_invoice(&invoice);
+/// let payment_id = PaymentId([42; 32]);
+/// match channel_manager.send_payment(
+///     payment_hash, recipient_onion, payment_id, route_params, retry
+/// ) {
+///     Ok(()) => println!("Sending payment with hash {}", payment_hash),
+///     Err(e) => println!("Failed sending payment with hash {}: {:?}", payment_hash, e),
+/// }
+///
+/// let expected_payment_id = payment_id;
+/// let expected_payment_hash = payment_hash;
+/// assert!(
+///     channel_manager.list_recent_payments().iter().find(|details| matches!(
+///         details,
+///         RecentPaymentDetails::Pending {
+///             payment_id: expected_payment_id,
+///             payment_hash: expected_payment_hash,
+///             ..
+///         }
+///     )).is_some()
+/// );
+///
+/// // On the event processing thread
+/// channel_manager.process_pending_events(&|event| match event {
+///     Event::PaymentSent { payment_hash, .. } => println!("Paid {}", payment_hash),
+///     Event::PaymentFailed { payment_hash, .. } => println!("Failed paying {}", payment_hash),
+///     // ...
+/// #     _ => {},
+/// });
+/// # }
+/// ```
+///
+/// ## BOLT 12 Offers
+///
+/// The [`offers`] module is useful for creating BOLT 12 offers. An [`Offer`] is a precursor to a
+/// [`Bolt12Invoice`], which must first be requested by the payer. The interchange of these messages
+/// as defined in the specification is handled by [`ChannelManager`] and its implementation of
+/// [`OffersMessageHandler`]. However, this only works with an [`Offer`] created using a builder
+/// returned by [`create_offer_builder`]. With this approach, BOLT 12 offers and invoices are
+/// stateless just as BOLT 11 invoices are.
+///
+/// ```
+/// # use lightning::events::{Event, EventsProvider, PaymentPurpose};
+/// # use lightning::ln::channelmanager::AChannelManager;
+/// # use lightning::offers::parse::Bolt12SemanticError;
+/// #
+/// # fn example<T: AChannelManager>(channel_manager: T) -> Result<(), Bolt12SemanticError> {
+/// # let channel_manager = channel_manager.get_cm();
+/// let offer = channel_manager
+///     .create_offer_builder()?
+/// # ;
+/// # // Needed for compiling for c_bindings
+/// # let builder: lightning::offers::offer::OfferBuilder<_, _> = offer.into();
+/// # let offer = builder
+///     .description("coffee".to_string())
+///     .amount_msats(10_000_000)
+///     .build()?;
+/// let bech32_offer = offer.to_string();
+///
+/// // On the event processing thread
+/// channel_manager.process_pending_events(&|event| match event {
+///     Event::PaymentClaimable { payment_hash, purpose, .. } => match purpose {
+///         PaymentPurpose::Bolt12OfferPayment { payment_preimage: Some(payment_preimage), .. } => {
+///             println!("Claiming payment {}", payment_hash);
+///             channel_manager.claim_funds(payment_preimage);
+///         },
+///         PaymentPurpose::Bolt12OfferPayment { payment_preimage: None, .. } => {
+///             println!("Unknown payment hash: {}", payment_hash);
+///         },
+///         // ...
+/// #         _ => {},
+///     },
+///     Event::PaymentClaimed { payment_hash, amount_msat, .. } => {
+///         println!("Claimed {} msats", amount_msat);
+///     },
+///     // ...
+/// #     _ => {},
+/// });
+/// # Ok(())
+/// # }
+/// ```
+///
+/// Use [`pay_for_offer`] to initiated payment, which sends an [`InvoiceRequest`] for an [`Offer`]
+/// and pays the [`Bolt12Invoice`] response. In addition to success and failure events,
+/// [`ChannelManager`] may also generate an [`Event::InvoiceRequestFailed`].
+///
+/// ```
+/// # use lightning::events::{Event, EventsProvider};
+/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, Retry};
+/// # use lightning::offers::offer::Offer;
+/// #
+/// # fn example<T: AChannelManager>(
+/// #     channel_manager: T, offer: &Offer, quantity: Option<u64>, amount_msats: Option<u64>,
+/// #     payer_note: Option<String>, retry: Retry, max_total_routing_fee_msat: Option<u64>
+/// # ) {
+/// # let channel_manager = channel_manager.get_cm();
+/// let payment_id = PaymentId([42; 32]);
+/// match channel_manager.pay_for_offer(
+///     offer, quantity, amount_msats, payer_note, payment_id, retry, max_total_routing_fee_msat
+/// ) {
+///     Ok(()) => println!("Requesting invoice for offer"),
+///     Err(e) => println!("Unable to request invoice for offer: {:?}", e),
+/// }
 ///
-/// Implements [`ChannelMessageHandler`], handling the multi-channel parts and passing things through
-/// to individual Channels.
+/// // First the payment will be waiting on an invoice
+/// let expected_payment_id = payment_id;
+/// assert!(
+///     channel_manager.list_recent_payments().iter().find(|details| matches!(
+///         details,
+///         RecentPaymentDetails::AwaitingInvoice { payment_id: expected_payment_id }
+///     )).is_some()
+/// );
+///
+/// // Once the invoice is received, a payment will be sent
+/// assert!(
+///     channel_manager.list_recent_payments().iter().find(|details| matches!(
+///         details,
+///         RecentPaymentDetails::Pending { payment_id: expected_payment_id, ..  }
+///     )).is_some()
+/// );
+///
+/// // On the event processing thread
+/// channel_manager.process_pending_events(&|event| match event {
+///     Event::PaymentSent { payment_id: Some(payment_id), .. } => println!("Paid {}", payment_id),
+///     Event::PaymentFailed { payment_id, .. } => println!("Failed paying {}", payment_id),
+///     Event::InvoiceRequestFailed { payment_id, .. } => println!("Failed paying {}", payment_id),
+///     // ...
+/// #     _ => {},
+/// });
+/// # }
+/// ```
+///
+/// ## BOLT 12 Refunds
+///
+/// A [`Refund`] is a request for an invoice to be paid. Like *paying* for an [`Offer`], *creating*
+/// a [`Refund`] involves maintaining state since it represents a future outbound payment.
+/// Therefore, use [`create_refund_builder`] when creating one, otherwise [`ChannelManager`] will
+/// refuse to pay any corresponding [`Bolt12Invoice`] that it receives.
+///
+/// ```
+/// # use core::time::Duration;
+/// # use lightning::events::{Event, EventsProvider};
+/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, Retry};
+/// # use lightning::offers::parse::Bolt12SemanticError;
+/// #
+/// # fn example<T: AChannelManager>(
+/// #     channel_manager: T, amount_msats: u64, absolute_expiry: Duration, retry: Retry,
+/// #     max_total_routing_fee_msat: Option<u64>
+/// # ) -> Result<(), Bolt12SemanticError> {
+/// # let channel_manager = channel_manager.get_cm();
+/// let payment_id = PaymentId([42; 32]);
+/// let refund = channel_manager
+///     .create_refund_builder(
+///         amount_msats, absolute_expiry, payment_id, retry, max_total_routing_fee_msat
+///     )?
+/// # ;
+/// # // Needed for compiling for c_bindings
+/// # let builder: lightning::offers::refund::RefundBuilder<_> = refund.into();
+/// # let refund = builder
+///     .description("coffee".to_string())
+///     .payer_note("refund for order 1234".to_string())
+///     .build()?;
+/// let bech32_refund = refund.to_string();
+///
+/// // First the payment will be waiting on an invoice
+/// let expected_payment_id = payment_id;
+/// assert!(
+///     channel_manager.list_recent_payments().iter().find(|details| matches!(
+///         details,
+///         RecentPaymentDetails::AwaitingInvoice { payment_id: expected_payment_id }
+///     )).is_some()
+/// );
+///
+/// // Once the invoice is received, a payment will be sent
+/// assert!(
+///     channel_manager.list_recent_payments().iter().find(|details| matches!(
+///         details,
+///         RecentPaymentDetails::Pending { payment_id: expected_payment_id, ..  }
+///     )).is_some()
+/// );
+///
+/// // On the event processing thread
+/// channel_manager.process_pending_events(&|event| match event {
+///     Event::PaymentSent { payment_id: Some(payment_id), .. } => println!("Paid {}", payment_id),
+///     Event::PaymentFailed { payment_id, .. } => println!("Failed paying {}", payment_id),
+///     // ...
+/// #     _ => {},
+/// });
+/// # Ok(())
+/// # }
+/// ```
+///
+/// Use [`request_refund_payment`] to send a [`Bolt12Invoice`] for receiving the refund. Similar to
+/// *creating* an [`Offer`], this is stateless as it represents an inbound payment.
+///
+/// ```
+/// # use lightning::events::{Event, EventsProvider, PaymentPurpose};
+/// # use lightning::ln::channelmanager::AChannelManager;
+/// # use lightning::offers::refund::Refund;
+/// #
+/// # fn example<T: AChannelManager>(channel_manager: T, refund: &Refund) {
+/// # let channel_manager = channel_manager.get_cm();
+/// let known_payment_hash = match channel_manager.request_refund_payment(refund) {
+///     Ok(invoice) => {
+///         let payment_hash = invoice.payment_hash();
+///         println!("Requesting refund payment {}", payment_hash);
+///         payment_hash
+///     },
+///     Err(e) => panic!("Unable to request payment for refund: {:?}", e),
+/// };
+///
+/// // On the event processing thread
+/// channel_manager.process_pending_events(&|event| match event {
+///     Event::PaymentClaimable { payment_hash, purpose, .. } => match purpose {
+///            PaymentPurpose::Bolt12RefundPayment { payment_preimage: Some(payment_preimage), .. } => {
+///             assert_eq!(payment_hash, known_payment_hash);
+///             println!("Claiming payment {}", payment_hash);
+///             channel_manager.claim_funds(payment_preimage);
+///         },
+///            PaymentPurpose::Bolt12RefundPayment { payment_preimage: None, .. } => {
+///             println!("Unknown payment hash: {}", payment_hash);
+///            },
+///         // ...
+/// #         _ => {},
+///     },
+///     Event::PaymentClaimed { payment_hash, amount_msat, .. } => {
+///         assert_eq!(payment_hash, known_payment_hash);
+///         println!("Claimed {} msats", amount_msat);
+///     },
+///     // ...
+/// #     _ => {},
+/// });
+/// # }
+/// ```
+///
+/// # Persistence
 ///
 /// Implements [`Writeable`] to write out all channel state to disk. Implies [`peer_disconnected`] for
 /// all peers during write/read (though does not modify this instance, only the instance being
@@ -1131,12 +1761,16 @@ where
 /// tells you the last block hash which was connected. You should get the best block tip before using the manager.
 /// See [`chain::Listen`] and [`chain::Confirm`] for more details.
 ///
+/// # `ChannelUpdate` Messages
+///
 /// Note that `ChannelManager` is responsible for tracking liveness of its channels and generating
 /// [`ChannelUpdate`] messages informing peers that the channel is temporarily disabled. To avoid
 /// spam due to quick disconnection/reconnection, updates are not sent until the channel has been
 /// offline for a full minute. In order to track this, you must call
 /// [`timer_tick_occurred`] roughly once per minute, though it doesn't have to be perfect.
 ///
+/// # DoS Mitigation
+///
 /// To avoid trivial DoS issues, `ChannelManager` limits the number of inbound connections and
 /// inbound channels without confirmed funding transactions. This may result in nodes which we do
 /// not have a channel with being unable to connect to us or open new channels with us if we have
@@ -1146,19 +1780,53 @@ where
 /// exempted from the count of unfunded channels. Similarly, outbound channels and connections are
 /// never limited. Please ensure you limit the count of such channels yourself.
 ///
+/// # Type Aliases
+///
 /// Rather than using a plain `ChannelManager`, it is preferable to use either a [`SimpleArcChannelManager`]
 /// a [`SimpleRefChannelManager`], for conciseness. See their documentation for more details, but
 /// essentially you should default to using a [`SimpleRefChannelManager`], and use a
 /// [`SimpleArcChannelManager`] when you require a `ChannelManager` with a static lifetime, such as when
 /// you're using lightning-net-tokio.
 ///
+/// [`ChainMonitor`]: crate::chain::chainmonitor::ChainMonitor
+/// [`MessageHandler`]: crate::ln::peer_handler::MessageHandler
+/// [`OnionMessenger`]: crate::onion_message::messenger::OnionMessenger
+/// [`PeerManager::read_event`]: crate::ln::peer_handler::PeerManager::read_event
+/// [`PeerManager::process_events`]: crate::ln::peer_handler::PeerManager::process_events
+/// [`timer_tick_occurred`]: Self::timer_tick_occurred
+/// [`get_and_clear_needs_persistence`]: Self::get_and_clear_needs_persistence
+/// [`Persister`]: crate::util::persist::Persister
+/// [`KVStore`]: crate::util::persist::KVStore
+/// [`get_event_or_persistence_needed_future`]: Self::get_event_or_persistence_needed_future
+/// [`lightning-block-sync`]: https://docs.rs/lightning_block_sync/latest/lightning_block_sync
+/// [`lightning-transaction-sync`]: https://docs.rs/lightning_transaction_sync/latest/lightning_transaction_sync
+/// [`lightning-background-processor`]: https://docs.rs/lightning_background_processor/lightning_background_processor
+/// [`list_channels`]: Self::list_channels
+/// [`list_usable_channels`]: Self::list_usable_channels
+/// [`create_channel`]: Self::create_channel
+/// [`close_channel`]: Self::force_close_broadcasting_latest_txn
+/// [`force_close_broadcasting_latest_txn`]: Self::force_close_broadcasting_latest_txn
+/// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md
+/// [BOLT 12]: https://github.com/rustyrussell/lightning-rfc/blob/guilt/offers/12-offer-encoding.md
+/// [`list_recent_payments`]: Self::list_recent_payments
+/// [`abandon_payment`]: Self::abandon_payment
+/// [`lightning-invoice`]: https://docs.rs/lightning_invoice/latest/lightning_invoice
+/// [`create_inbound_payment`]: Self::create_inbound_payment
+/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
+/// [`claim_funds`]: Self::claim_funds
+/// [`send_payment`]: Self::send_payment
+/// [`offers`]: crate::offers
+/// [`create_offer_builder`]: Self::create_offer_builder
+/// [`pay_for_offer`]: Self::pay_for_offer
+/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+/// [`create_refund_builder`]: Self::create_refund_builder
+/// [`request_refund_payment`]: Self::request_refund_payment
 /// [`peer_disconnected`]: msgs::ChannelMessageHandler::peer_disconnected
 /// [`funding_created`]: msgs::FundingCreated
 /// [`funding_transaction_generated`]: Self::funding_transaction_generated
 /// [`BlockHash`]: bitcoin::hash_types::BlockHash
 /// [`update_channel`]: chain::Watch::update_channel
 /// [`ChannelUpdate`]: msgs::ChannelUpdate
-/// [`timer_tick_occurred`]: Self::timer_tick_occurred
 /// [`read`]: ReadableArgs::read
 //
 // Lock order:
@@ -1178,6 +1846,8 @@ where
 //  |   |
 //  |   |__`pending_intercepted_htlcs`
 //  |
+//  |__`decode_update_add_htlcs`
+//  |
 //  |__`per_peer_state`
 //      |
 //      |__`pending_inbound_payments`
@@ -1268,6 +1938,18 @@ where
        /// See `ChannelManager` struct-level documentation for lock order requirements.
        pending_intercepted_htlcs: Mutex<HashMap<InterceptId, PendingAddHTLCInfo>>,
 
+       /// SCID/SCID Alias -> pending `update_add_htlc`s to decode.
+       ///
+       /// Note that because we may have an SCID Alias as the key we can have two entries per channel,
+       /// though in practice we probably won't be receiving HTLCs for a channel both via the alias
+       /// and via the classic SCID.
+       ///
+       /// Note that no consistency guarantees are made about the existence of a channel with the
+       /// `short_channel_id` here, nor the `channel_id` in `UpdateAddHTLC`!
+       ///
+       /// See `ChannelManager` struct-level documentation for lock order requirements.
+       decode_update_add_htlcs: Mutex<HashMap<u64, Vec<msgs::UpdateAddHTLC>>>,
+
        /// The sets of payments which are claimable or currently being claimed. See
        /// [`ClaimablePayments`]' individual field docs for more info.
        ///
@@ -1411,6 +2093,9 @@ where
 
        pending_offers_messages: Mutex<Vec<PendingOnionMessage<OffersMessage>>>,
 
+       /// Tracks the message events that are to be broadcasted when we are connected to some peer.
+       pending_broadcast_messages: Mutex<Vec<MessageSendEvent>>,
+
        entropy_source: ES,
        node_signer: NS,
        signer_provider: SP,
@@ -2002,19 +2687,20 @@ macro_rules! handle_error {
                match $internal {
                        Ok(msg) => Ok(msg),
                        Err(MsgHandleErrInternal { err, shutdown_finish, .. }) => {
-                               let mut msg_events = Vec::with_capacity(2);
+                               let mut msg_event = None;
 
                                if let Some((shutdown_res, update_option)) = shutdown_finish {
                                        let counterparty_node_id = shutdown_res.counterparty_node_id;
                                        let channel_id = shutdown_res.channel_id;
                                        let logger = WithContext::from(
-                                               &$self.logger, Some(counterparty_node_id), Some(channel_id),
+                                               &$self.logger, Some(counterparty_node_id), Some(channel_id), None
                                        );
                                        log_error!(logger, "Force-closing channel: {}", err.err);
 
                                        $self.finish_close_channel(shutdown_res);
                                        if let Some(update) = update_option {
-                                               msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
+                                               let mut pending_broadcast_messages = $self.pending_broadcast_messages.lock().unwrap();
+                                               pending_broadcast_messages.push(events::MessageSendEvent::BroadcastChannelUpdate {
                                                        msg: update
                                                });
                                        }
@@ -2024,17 +2710,17 @@ macro_rules! handle_error {
 
                                if let msgs::ErrorAction::IgnoreError = err.action {
                                } else {
-                                       msg_events.push(events::MessageSendEvent::HandleError {
+                                       msg_event = Some(events::MessageSendEvent::HandleError {
                                                node_id: $counterparty_node_id,
                                                action: err.action.clone()
                                        });
                                }
 
-                               if !msg_events.is_empty() {
+                               if let Some(msg_event) = msg_event {
                                        let per_peer_state = $self.per_peer_state.read().unwrap();
                                        if let Some(peer_state_mutex) = per_peer_state.get(&$counterparty_node_id) {
                                                let mut peer_state = peer_state_mutex.lock().unwrap();
-                                               peer_state.pending_msg_events.append(&mut msg_events);
+                                               peer_state.pending_msg_events.push(msg_event);
                                        }
                                }
 
@@ -2078,7 +2764,7 @@ macro_rules! convert_chan_phase_err {
                                (false, MsgHandleErrInternal::from_chan_no_close(ChannelError::Ignore(msg), *$channel_id))
                        },
                        ChannelError::Close(msg) => {
-                               let logger = WithChannelContext::from(&$self.logger, &$channel.context);
+                               let logger = WithChannelContext::from(&$self.logger, &$channel.context, None);
                                log_error!(logger, "Closing channel {} due to close-required error: {}", $channel_id, msg);
                                update_maps_on_chan_removal!($self, $channel.context);
                                let reason = ClosureReason::ProcessingError { err: msg.clone() };
@@ -2106,11 +2792,11 @@ macro_rules! convert_chan_phase_err {
                        ChannelPhase::UnfundedInboundV1(channel) => {
                                convert_chan_phase_err!($self, $err, channel, $channel_id, UNFUNDED_CHANNEL)
                        },
-                       #[cfg(dual_funding)]
+                       #[cfg(any(dual_funding, splicing))]
                        ChannelPhase::UnfundedOutboundV2(channel) => {
                                convert_chan_phase_err!($self, $err, channel, $channel_id, UNFUNDED_CHANNEL)
                        },
-                       #[cfg(dual_funding)]
+                       #[cfg(any(dual_funding, splicing))]
                        ChannelPhase::UnfundedInboundV2(channel) => {
                                convert_chan_phase_err!($self, $err, channel, $channel_id, UNFUNDED_CHANNEL)
                        },
@@ -2213,7 +2899,7 @@ macro_rules! emit_channel_ready_event {
 
 macro_rules! handle_monitor_update_completion {
        ($self: ident, $peer_state_lock: expr, $peer_state: expr, $per_peer_state_lock: expr, $chan: expr) => { {
-               let logger = WithChannelContext::from(&$self.logger, &$chan.context);
+               let logger = WithChannelContext::from(&$self.logger, &$chan.context, None);
                let mut updates = $chan.monitor_updating_restored(&&logger,
                        &$self.node_signer, $self.chain_hash, &$self.default_configuration,
                        $self.best_block.read().unwrap().height);
@@ -2235,9 +2921,9 @@ macro_rules! handle_monitor_update_completion {
                let update_actions = $peer_state.monitor_update_blocked_actions
                        .remove(&$chan.context.channel_id()).unwrap_or(Vec::new());
 
-               let htlc_forwards = $self.handle_channel_resumption(
+               let (htlc_forwards, decode_update_add_htlcs) = $self.handle_channel_resumption(
                        &mut $peer_state.pending_msg_events, $chan, updates.raa,
-                       updates.commitment_update, updates.order, updates.accepted_htlcs,
+                       updates.commitment_update, updates.order, updates.accepted_htlcs, updates.pending_update_adds,
                        updates.funding_broadcastable, updates.channel_ready,
                        updates.announcement_sigs);
                if let Some(upd) = channel_update {
@@ -2298,6 +2984,9 @@ macro_rules! handle_monitor_update_completion {
                if let Some(forwards) = htlc_forwards {
                        $self.forward_htlcs(&mut [forwards][..]);
                }
+               if let Some(decode) = decode_update_add_htlcs {
+                       $self.push_decode_update_add_htlcs(decode);
+               }
                $self.finalize_claims(updates.finalized_claimed_htlcs);
                for failure in updates.failed_htlcs.drain(..) {
                        let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
@@ -2309,7 +2998,7 @@ macro_rules! handle_monitor_update_completion {
 macro_rules! handle_new_monitor_update {
        ($self: ident, $update_res: expr, $chan: expr, _internal, $completed: expr) => { {
                debug_assert!($self.background_events_processed_since_startup.load(Ordering::Acquire));
-               let logger = WithChannelContext::from(&$self.logger, &$chan.context);
+               let logger = WithChannelContext::from(&$self.logger, &$chan.context, None);
                match $update_res {
                        ChannelMonitorUpdateStatus::UnrecoverableError => {
                                let err_str = "ChannelMonitor[Update] persistence failed unrecoverably. This indicates we cannot continue normal operation and must shut down.";
@@ -2474,6 +3163,7 @@ where
                        pending_inbound_payments: Mutex::new(new_hash_map()),
                        pending_outbound_payments: OutboundPayments::new(),
                        forward_htlcs: Mutex::new(new_hash_map()),
+                       decode_update_add_htlcs: Mutex::new(new_hash_map()),
                        claimable_payments: Mutex::new(ClaimablePayments { claimable_payments: new_hash_map(), pending_claiming_payments: new_hash_map() }),
                        pending_intercepted_htlcs: Mutex::new(new_hash_map()),
                        outpoint_to_peer: Mutex::new(new_hash_map()),
@@ -2501,6 +3191,7 @@ where
                        funding_batch_states: Mutex::new(BTreeMap::new()),
 
                        pending_offers_messages: Mutex::new(Vec::new()),
+                       pending_broadcast_messages: Mutex::new(Vec::new()),
 
                        entropy_source,
                        node_signer,
@@ -2888,7 +3579,7 @@ where
                }
 
                let logger = WithContext::from(
-                       &self.logger, Some(shutdown_res.counterparty_node_id), Some(shutdown_res.channel_id),
+                       &self.logger, Some(shutdown_res.counterparty_node_id), Some(shutdown_res.channel_id), None
                );
 
                log_debug!(logger, "Finishing closure of channel due to {} with {} HTLCs to fail",
@@ -2964,7 +3655,7 @@ where
                        } else {
                                ClosureReason::HolderForceClosed
                        };
-                       let logger = WithContext::from(&self.logger, Some(*peer_node_id), Some(*channel_id));
+                       let logger = WithContext::from(&self.logger, Some(*peer_node_id), Some(*channel_id), None);
                        if let hash_map::Entry::Occupied(chan_phase_entry) = peer_state.channel_by_id.entry(channel_id.clone()) {
                                log_error!(logger, "Force-closing channel {}", channel_id);
                                let mut chan_phase = remove_channel_phase!(self, chan_phase_entry);
@@ -2980,8 +3671,8 @@ where
                                                // Unfunded channel has no update
                                                (None, chan_phase.context().get_counterparty_node_id())
                                        },
-                                       // TODO(dual_funding): Combine this match arm with above once #[cfg(dual_funding)] is removed.
-                                       #[cfg(dual_funding)]
+                                       // TODO(dual_funding): Combine this match arm with above once #[cfg(any(dual_funding, splicing))] is removed.
+                                       #[cfg(any(dual_funding, splicing))]
                                        ChannelPhase::UnfundedOutboundV2(_) | ChannelPhase::UnfundedInboundV2(_) => {
                                                self.finish_close_channel(chan_phase.context_mut().force_shutdown(false, closure_reason));
                                                // Unfunded channel has no update
@@ -2999,17 +3690,11 @@ where
                        }
                };
                if let Some(update) = update_opt {
-                       // Try to send the `BroadcastChannelUpdate` to the peer we just force-closed on, but if
-                       // not try to broadcast it via whatever peer we have.
-                       let per_peer_state = self.per_peer_state.read().unwrap();
-                       let a_peer_state_opt = per_peer_state.get(peer_node_id)
-                               .ok_or(per_peer_state.values().next());
-                       if let Ok(a_peer_state_mutex) = a_peer_state_opt {
-                               let mut a_peer_state = a_peer_state_mutex.lock().unwrap();
-                               a_peer_state.pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
-                                       msg: update
-                               });
-                       }
+                       // If we have some Channel Update to broadcast, we cache it and broadcast it later.
+                       let mut pending_broadcast_messages = self.pending_broadcast_messages.lock().unwrap();
+                       pending_broadcast_messages.push(events::MessageSendEvent::BroadcastChannelUpdate {
+                               msg: update
+                       });
                }
 
                Ok(counterparty_node_id)
@@ -3073,6 +3758,163 @@ where
                }
        }
 
+       fn can_forward_htlc_to_outgoing_channel(
+               &self, chan: &mut Channel<SP>, msg: &msgs::UpdateAddHTLC, next_packet: &NextPacketDetails
+       ) -> Result<(), (&'static str, u16, Option<msgs::ChannelUpdate>)> {
+               if !chan.context.should_announce() && !self.default_configuration.accept_forwards_to_priv_channels {
+                       // Note that the behavior here should be identical to the above block - we
+                       // should NOT reveal the existence or non-existence of a private channel if
+                       // we don't allow forwards outbound over them.
+                       return Err(("Refusing to forward to a private channel based on our config.", 0x4000 | 10, None));
+               }
+               if chan.context.get_channel_type().supports_scid_privacy() && next_packet.outgoing_scid != chan.context.outbound_scid_alias() {
+                       // `option_scid_alias` (referred to in LDK as `scid_privacy`) means
+                       // "refuse to forward unless the SCID alias was used", so we pretend
+                       // we don't have the channel here.
+                       return Err(("Refusing to forward over real channel SCID as our counterparty requested.", 0x4000 | 10, None));
+               }
+
+               // Note that we could technically not return an error yet here and just hope
+               // that the connection is reestablished or monitor updated by the time we get
+               // around to doing the actual forward, but better to fail early if we can and
+               // hopefully an attacker trying to path-trace payments cannot make this occur
+               // on a small/per-node/per-channel scale.
+               if !chan.context.is_live() { // channel_disabled
+                       // If the channel_update we're going to return is disabled (i.e. the
+                       // peer has been disabled for some time), return `channel_disabled`,
+                       // otherwise return `temporary_channel_failure`.
+                       let chan_update_opt = self.get_channel_update_for_onion(next_packet.outgoing_scid, chan).ok();
+                       if chan_update_opt.as_ref().map(|u| u.contents.flags & 2 == 2).unwrap_or(false) {
+                               return Err(("Forwarding channel has been disconnected for some time.", 0x1000 | 20, chan_update_opt));
+                       } else {
+                               return Err(("Forwarding channel is not in a ready state.", 0x1000 | 7, chan_update_opt));
+                       }
+               }
+               if next_packet.outgoing_amt_msat < chan.context.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
+                       let chan_update_opt = self.get_channel_update_for_onion(next_packet.outgoing_scid, chan).ok();
+                       return Err(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, chan_update_opt));
+               }
+               if let Err((err, code)) = chan.htlc_satisfies_config(msg, next_packet.outgoing_amt_msat, next_packet.outgoing_cltv_value) {
+                       let chan_update_opt = self.get_channel_update_for_onion(next_packet.outgoing_scid, chan).ok();
+                       return Err((err, code, chan_update_opt));
+               }
+
+               Ok(())
+       }
+
+       /// Executes a callback `C` that returns some value `X` on the channel found with the given
+       /// `scid`. `None` is returned when the channel is not found.
+       fn do_funded_channel_callback<X, C: Fn(&mut Channel<SP>) -> X>(
+               &self, scid: u64, callback: C,
+       ) -> Option<X> {
+               let (counterparty_node_id, channel_id) = match self.short_to_chan_info.read().unwrap().get(&scid).cloned() {
+                       None => return None,
+                       Some((cp_id, id)) => (cp_id, id),
+               };
+               let per_peer_state = self.per_peer_state.read().unwrap();
+               let peer_state_mutex_opt = per_peer_state.get(&counterparty_node_id);
+               if peer_state_mutex_opt.is_none() {
+                       return None;
+               }
+               let mut peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
+               let peer_state = &mut *peer_state_lock;
+               match peer_state.channel_by_id.get_mut(&channel_id).and_then(
+                       |chan_phase| if let ChannelPhase::Funded(chan) = chan_phase { Some(chan) } else { None }
+               ) {
+                       None => None,
+                       Some(chan) => Some(callback(chan)),
+               }
+       }
+
+       fn can_forward_htlc(
+               &self, msg: &msgs::UpdateAddHTLC, next_packet_details: &NextPacketDetails
+       ) -> Result<(), (&'static str, u16, Option<msgs::ChannelUpdate>)> {
+               match self.do_funded_channel_callback(next_packet_details.outgoing_scid, |chan: &mut Channel<SP>| {
+                       self.can_forward_htlc_to_outgoing_channel(chan, msg, next_packet_details)
+               }) {
+                       Some(Ok(())) => {},
+                       Some(Err(e)) => return Err(e),
+                       None => {
+                               // If we couldn't find the channel info for the scid, it may be a phantom or
+                               // intercept forward.
+                               if (self.default_configuration.accept_intercept_htlcs &&
+                                       fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, next_packet_details.outgoing_scid, &self.chain_hash)) ||
+                                       fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, next_packet_details.outgoing_scid, &self.chain_hash)
+                               {} else {
+                                       return Err(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
+                               }
+                       }
+               }
+
+               let cur_height = self.best_block.read().unwrap().height + 1;
+               if let Err((err_msg, err_code)) = check_incoming_htlc_cltv(
+                       cur_height, next_packet_details.outgoing_cltv_value, msg.cltv_expiry
+               ) {
+                       let chan_update_opt = self.do_funded_channel_callback(next_packet_details.outgoing_scid, |chan: &mut Channel<SP>| {
+                               self.get_channel_update_for_onion(next_packet_details.outgoing_scid, chan).ok()
+                       }).flatten();
+                       return Err((err_msg, err_code, chan_update_opt));
+               }
+
+               Ok(())
+       }
+
+       fn htlc_failure_from_update_add_err(
+               &self, msg: &msgs::UpdateAddHTLC, counterparty_node_id: &PublicKey, err_msg: &'static str,
+               mut err_code: u16, chan_update: Option<msgs::ChannelUpdate>, is_intro_node_blinded_forward: bool,
+               shared_secret: &[u8; 32]
+       ) -> HTLCFailureMsg {
+               let mut res = VecWriter(Vec::with_capacity(chan_update.serialized_length() + 2 + 8 + 2));
+               if chan_update.is_some() && err_code & 0x1000 == 0x1000 {
+                       let chan_update = chan_update.unwrap();
+                       if err_code == 0x1000 | 11 || err_code == 0x1000 | 12 {
+                               msg.amount_msat.write(&mut res).expect("Writes cannot fail");
+                       }
+                       else if err_code == 0x1000 | 13 {
+                               msg.cltv_expiry.write(&mut res).expect("Writes cannot fail");
+                       }
+                       else if err_code == 0x1000 | 20 {
+                               // TODO: underspecified, follow https://github.com/lightning/bolts/issues/791
+                               0u16.write(&mut res).expect("Writes cannot fail");
+                       }
+                       (chan_update.serialized_length() as u16 + 2).write(&mut res).expect("Writes cannot fail");
+                       msgs::ChannelUpdate::TYPE.write(&mut res).expect("Writes cannot fail");
+                       chan_update.write(&mut res).expect("Writes cannot fail");
+               } else if err_code & 0x1000 == 0x1000 {
+                       // If we're trying to return an error that requires a `channel_update` but
+                       // we're forwarding to a phantom or intercept "channel" (i.e. cannot
+                       // generate an update), just use the generic "temporary_node_failure"
+                       // instead.
+                       err_code = 0x2000 | 2;
+               }
+
+               log_info!(
+                       WithContext::from(&self.logger, Some(*counterparty_node_id), Some(msg.channel_id), Some(msg.payment_hash)),
+                       "Failed to accept/forward incoming HTLC: {}", err_msg
+               );
+               // If `msg.blinding_point` is set, we must always fail with malformed.
+               if msg.blinding_point.is_some() {
+                       return HTLCFailureMsg::Malformed(msgs::UpdateFailMalformedHTLC {
+                               channel_id: msg.channel_id,
+                               htlc_id: msg.htlc_id,
+                               sha256_of_onion: [0; 32],
+                               failure_code: INVALID_ONION_BLINDING,
+                       });
+               }
+
+               let (err_code, err_data) = if is_intro_node_blinded_forward {
+                       (INVALID_ONION_BLINDING, &[0; 32][..])
+               } else {
+                       (err_code, &res.0[..])
+               };
+               HTLCFailureMsg::Relay(msgs::UpdateFailHTLC {
+                       channel_id: msg.channel_id,
+                       htlc_id: msg.htlc_id,
+                       reason: HTLCFailReason::reason(err_code, err_data.to_vec())
+                               .get_encrypted_failure_packet(shared_secret, &None),
+               })
+       }
+
        fn decode_update_add_htlc_onion(
                &self, msg: &msgs::UpdateAddHTLC, counterparty_node_id: &PublicKey,
        ) -> Result<
@@ -3082,48 +3924,7 @@ where
                        msg, &self.node_signer, &self.logger, &self.secp_ctx
                )?;
 
-               let is_intro_node_forward = match next_hop {
-                       onion_utils::Hop::Forward {
-                               next_hop_data: msgs::InboundOnionPayload::BlindedForward {
-                                       intro_node_blinding_point: Some(_), ..
-                               }, ..
-                       } => true,
-                       _ => false,
-               };
-
-               macro_rules! return_err {
-                       ($msg: expr, $err_code: expr, $data: expr) => {
-                               {
-                                       log_info!(
-                                               WithContext::from(&self.logger, Some(*counterparty_node_id), Some(msg.channel_id)),
-                                               "Failed to accept/forward incoming HTLC: {}", $msg
-                                       );
-                                       // If `msg.blinding_point` is set, we must always fail with malformed.
-                                       if msg.blinding_point.is_some() {
-                                               return Err(HTLCFailureMsg::Malformed(msgs::UpdateFailMalformedHTLC {
-                                                       channel_id: msg.channel_id,
-                                                       htlc_id: msg.htlc_id,
-                                                       sha256_of_onion: [0; 32],
-                                                       failure_code: INVALID_ONION_BLINDING,
-                                               }));
-                                       }
-
-                                       let (err_code, err_data) = if is_intro_node_forward {
-                                               (INVALID_ONION_BLINDING, &[0; 32][..])
-                                       } else { ($err_code, $data) };
-                                       return Err(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC {
-                                               channel_id: msg.channel_id,
-                                               htlc_id: msg.htlc_id,
-                                               reason: HTLCFailReason::reason(err_code, err_data.to_vec())
-                                                       .get_encrypted_failure_packet(&shared_secret, &None),
-                                       }));
-                               }
-                       }
-               }
-
-               let NextPacketDetails {
-                       next_packet_pubkey, outgoing_amt_msat, outgoing_scid, outgoing_cltv_value
-               } = match next_packet_details_opt {
+               let next_packet_details = match next_packet_details_opt {
                        Some(next_packet_details) => next_packet_details,
                        // it is a receive, so no need for outbound checks
                        None => return Ok((next_hop, shared_secret, None)),
@@ -3131,124 +3932,15 @@ where
 
                // Perform outbound checks here instead of in [`Self::construct_pending_htlc_info`] because we
                // can't hold the outbound peer state lock at the same time as the inbound peer state lock.
-               if let Some((err, mut code, chan_update)) = loop {
-                       let id_option = self.short_to_chan_info.read().unwrap().get(&outgoing_scid).cloned();
-                       let forwarding_chan_info_opt = match id_option {
-                               None => { // unknown_next_peer
-                                       // Note that this is likely a timing oracle for detecting whether an scid is a
-                                       // phantom or an intercept.
-                                       if (self.default_configuration.accept_intercept_htlcs &&
-                                               fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, outgoing_scid, &self.chain_hash)) ||
-                                               fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, outgoing_scid, &self.chain_hash)
-                                       {
-                                               None
-                                       } else {
-                                               break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
-                                       }
-                               },
-                               Some((cp_id, id)) => Some((cp_id.clone(), id.clone())),
-                       };
-                       let chan_update_opt = if let Some((counterparty_node_id, forwarding_id)) = forwarding_chan_info_opt {
-                               let per_peer_state = self.per_peer_state.read().unwrap();
-                               let peer_state_mutex_opt = per_peer_state.get(&counterparty_node_id);
-                               if peer_state_mutex_opt.is_none() {
-                                       break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
-                               }
-                               let mut peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
-                               let peer_state = &mut *peer_state_lock;
-                               let chan = match peer_state.channel_by_id.get_mut(&forwarding_id).map(
-                                       |chan_phase| if let ChannelPhase::Funded(chan) = chan_phase { Some(chan) } else { None }
-                               ).flatten() {
-                                       None => {
-                                               // Channel was removed. The short_to_chan_info and channel_by_id maps
-                                               // have no consistency guarantees.
-                                               break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
-                                       },
-                                       Some(chan) => chan
-                               };
-                               if !chan.context.should_announce() && !self.default_configuration.accept_forwards_to_priv_channels {
-                                       // Note that the behavior here should be identical to the above block - we
-                                       // should NOT reveal the existence or non-existence of a private channel if
-                                       // we don't allow forwards outbound over them.
-                                       break Some(("Refusing to forward to a private channel based on our config.", 0x4000 | 10, None));
-                               }
-                               if chan.context.get_channel_type().supports_scid_privacy() && outgoing_scid != chan.context.outbound_scid_alias() {
-                                       // `option_scid_alias` (referred to in LDK as `scid_privacy`) means
-                                       // "refuse to forward unless the SCID alias was used", so we pretend
-                                       // we don't have the channel here.
-                                       break Some(("Refusing to forward over real channel SCID as our counterparty requested.", 0x4000 | 10, None));
-                               }
-                               let chan_update_opt = self.get_channel_update_for_onion(outgoing_scid, chan).ok();
-
-                               // Note that we could technically not return an error yet here and just hope
-                               // that the connection is reestablished or monitor updated by the time we get
-                               // around to doing the actual forward, but better to fail early if we can and
-                               // hopefully an attacker trying to path-trace payments cannot make this occur
-                               // on a small/per-node/per-channel scale.
-                               if !chan.context.is_live() { // channel_disabled
-                                       // If the channel_update we're going to return is disabled (i.e. the
-                                       // peer has been disabled for some time), return `channel_disabled`,
-                                       // otherwise return `temporary_channel_failure`.
-                                       if chan_update_opt.as_ref().map(|u| u.contents.flags & 2 == 2).unwrap_or(false) {
-                                               break Some(("Forwarding channel has been disconnected for some time.", 0x1000 | 20, chan_update_opt));
-                                       } else {
-                                               break Some(("Forwarding channel is not in a ready state.", 0x1000 | 7, chan_update_opt));
-                                       }
-                               }
-                               if outgoing_amt_msat < chan.context.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
-                                       break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, chan_update_opt));
-                               }
-                               if let Err((err, code)) = chan.htlc_satisfies_config(&msg, outgoing_amt_msat, outgoing_cltv_value) {
-                                       break Some((err, code, chan_update_opt));
-                               }
-                               chan_update_opt
-                       } else {
-                               None
-                       };
-
-                       let cur_height = self.best_block.read().unwrap().height + 1;
-
-                       if let Err((err_msg, code)) = check_incoming_htlc_cltv(
-                               cur_height, outgoing_cltv_value, msg.cltv_expiry
-                       ) {
-                               if code & 0x1000 != 0 && chan_update_opt.is_none() {
-                                       // We really should set `incorrect_cltv_expiry` here but as we're not
-                                       // forwarding over a real channel we can't generate a channel_update
-                                       // for it. Instead we just return a generic temporary_node_failure.
-                                       break Some((err_msg, 0x2000 | 2, None))
-                               }
-                               let chan_update_opt = if code & 0x1000 != 0 { chan_update_opt } else { None };
-                               break Some((err_msg, code, chan_update_opt));
-                       }
+               self.can_forward_htlc(&msg, &next_packet_details).map_err(|e| {
+                       let (err_msg, err_code, chan_update_opt) = e;
+                       self.htlc_failure_from_update_add_err(
+                               msg, counterparty_node_id, err_msg, err_code, chan_update_opt,
+                               next_hop.is_intro_node_blinded_forward(), &shared_secret
+                       )
+               })?;
 
-                       break None;
-               }
-               {
-                       let mut res = VecWriter(Vec::with_capacity(chan_update.serialized_length() + 2 + 8 + 2));
-                       if let Some(chan_update) = chan_update {
-                               if code == 0x1000 | 11 || code == 0x1000 | 12 {
-                                       msg.amount_msat.write(&mut res).expect("Writes cannot fail");
-                               }
-                               else if code == 0x1000 | 13 {
-                                       msg.cltv_expiry.write(&mut res).expect("Writes cannot fail");
-                               }
-                               else if code == 0x1000 | 20 {
-                                       // TODO: underspecified, follow https://github.com/lightning/bolts/issues/791
-                                       0u16.write(&mut res).expect("Writes cannot fail");
-                               }
-                               (chan_update.serialized_length() as u16 + 2).write(&mut res).expect("Writes cannot fail");
-                               msgs::ChannelUpdate::TYPE.write(&mut res).expect("Writes cannot fail");
-                               chan_update.write(&mut res).expect("Writes cannot fail");
-                       } else if code & 0x1000 == 0x1000 {
-                               // If we're trying to return an error that requires a `channel_update` but
-                               // we're forwarding to a phantom or intercept "channel" (i.e. cannot
-                               // generate an update), just use the generic "temporary_node_failure"
-                               // instead.
-                               code = 0x2000 | 2;
-                       }
-                       return_err!(err, code, &res.0[..]);
-               }
-               Ok((next_hop, shared_secret, Some(next_packet_pubkey)))
+               Ok((next_hop, shared_secret, Some(next_packet_details.next_packet_pubkey)))
        }
 
        fn construct_pending_htlc_status<'a>(
@@ -3259,7 +3951,7 @@ where
                macro_rules! return_err {
                        ($msg: expr, $err_code: expr, $data: expr) => {
                                {
-                                       let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(msg.channel_id));
+                                       let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(msg.channel_id), Some(msg.payment_hash));
                                        log_info!(logger, "Failed to accept/forward incoming HTLC: {}", $msg);
                                        if msg.blinding_point.is_some() {
                                                return PendingHTLCStatus::Fail(HTLCFailureMsg::Malformed(
@@ -3328,7 +4020,7 @@ where
                if chan.context.get_short_channel_id().is_none() {
                        return Err(LightningError{err: "Channel not yet established".to_owned(), action: msgs::ErrorAction::IgnoreError});
                }
-               let logger = WithChannelContext::from(&self.logger, &chan.context);
+               let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                log_trace!(logger, "Attempting to generate broadcast channel update for channel {}", &chan.context.channel_id());
                self.get_channel_update_for_unicast(chan)
        }
@@ -3345,7 +4037,7 @@ where
        /// [`channel_update`]: msgs::ChannelUpdate
        /// [`internal_closing_signed`]: Self::internal_closing_signed
        fn get_channel_update_for_unicast(&self, chan: &Channel<SP>) -> Result<msgs::ChannelUpdate, LightningError> {
-               let logger = WithChannelContext::from(&self.logger, &chan.context);
+               let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                log_trace!(logger, "Attempting to generate channel update for channel {}", chan.context.channel_id());
                let short_channel_id = match chan.context.get_short_channel_id().or(chan.context.latest_inbound_scid_alias()) {
                        None => return Err(LightningError{err: "Channel not yet established".to_owned(), action: msgs::ErrorAction::IgnoreError}),
@@ -3356,7 +4048,7 @@ where
        }
 
        fn get_channel_update_for_onion(&self, short_channel_id: u64, chan: &Channel<SP>) -> Result<msgs::ChannelUpdate, LightningError> {
-               let logger = WithChannelContext::from(&self.logger, &chan.context);
+               let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                log_trace!(logger, "Generating channel update for channel {}", chan.context.channel_id());
                let were_node_one = self.our_network_pubkey.serialize()[..] < chan.context.get_counterparty_node_id().serialize()[..];
 
@@ -3395,8 +4087,8 @@ where
        pub(crate) fn test_send_payment_along_path(&self, path: &Path, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
                let _lck = self.total_consistency_lock.read().unwrap();
                self.send_payment_along_path(SendAlongPathArgs {
-                       path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
-                       session_priv_bytes
+                       path, payment_hash, recipient_onion: &recipient_onion, total_value,
+                       cur_height, payment_id, keysend_preimage, session_priv_bytes
                })
        }
 
@@ -3414,7 +4106,7 @@ where
                        &self.secp_ctx, &path, &session_priv, total_value, recipient_onion, cur_height,
                        payment_hash, keysend_preimage, prng_seed
                ).map_err(|e| {
-                       let logger = WithContext::from(&self.logger, Some(path.hops.first().unwrap().pubkey), None);
+                       let logger = WithContext::from(&self.logger, Some(path.hops.first().unwrap().pubkey), None, Some(*payment_hash));
                        log_error!(logger, "Failed to build an onion for path for payment hash {}", payment_hash);
                        e
                })?;
@@ -3422,14 +4114,14 @@ where
                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) {
                                None => {
-                                       let logger = WithContext::from(&self.logger, Some(path.hops.first().unwrap().pubkey), None);
+                                       let logger = WithContext::from(&self.logger, Some(path.hops.first().unwrap().pubkey), None, Some(*payment_hash));
                                        log_error!(logger, "Failed to find first-hop for payment hash {}", payment_hash);
                                        return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!".to_owned()})
                                },
                                Some((cp_id, chan_id)) => (cp_id.clone(), chan_id.clone()),
                        };
 
-                       let logger = WithContext::from(&self.logger, Some(counterparty_node_id), Some(id));
+                       let logger = WithContext::from(&self.logger, Some(counterparty_node_id), Some(id), Some(*payment_hash));
                        log_trace!(logger,
                                "Attempting to send payment with payment hash {} along path with next hop {}",
                                payment_hash, path.hops.first().unwrap().short_channel_id);
@@ -3446,7 +4138,7 @@ where
                                                        return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected".to_owned()});
                                                }
                                                let funding_txo = chan.context.get_funding_txo().unwrap();
-                                               let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                               let logger = WithChannelContext::from(&self.logger, &chan.context, Some(*payment_hash));
                                                let send_res = chan.send_htlc_and_commit(htlc_msat, payment_hash.clone(),
                                                        htlc_cltv, HTLCSource::OutboundRoute {
                                                                path: path.clone(),
@@ -3794,7 +4486,7 @@ where
 
        /// Handles the generation of a funding transaction, optionally (for tests) with a function
        /// which checks the correctness of the funding transaction given the associated channel.
-       fn funding_transaction_generated_intern<FundingOutput: FnMut(&OutboundV1Channel<SP>, &Transaction) -> Result<OutPoint, APIError>>(
+       fn funding_transaction_generated_intern<FundingOutput: FnMut(&OutboundV1Channel<SP>, &Transaction) -> Result<OutPoint, &'static str>>(
                &self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction, is_batch_funding: bool,
                mut find_funding_output: FundingOutput,
        ) -> Result<(), APIError> {
@@ -3807,26 +4499,38 @@ where
                let funding_txo;
                let (mut chan, msg_opt) = match peer_state.channel_by_id.remove(temporary_channel_id) {
                        Some(ChannelPhase::UnfundedOutboundV1(mut chan)) => {
-                               funding_txo = find_funding_output(&chan, &funding_transaction)?;
-
-                               let logger = WithChannelContext::from(&self.logger, &chan.context);
-                               let funding_res = chan.get_funding_created(funding_transaction, funding_txo, is_batch_funding, &&logger)
-                                       .map_err(|(mut chan, e)| if let ChannelError::Close(msg) = e {
-                                               let channel_id = chan.context.channel_id();
+                               macro_rules! close_chan { ($err: expr, $api_err: expr, $chan: expr) => { {
+                                       let counterparty;
+                                       let err = if let ChannelError::Close(msg) = $err {
+                                               let channel_id = $chan.context.channel_id();
+                                               counterparty = chan.context.get_counterparty_node_id();
                                                let reason = ClosureReason::ProcessingError { err: msg.clone() };
-                                               let shutdown_res = chan.context.force_shutdown(false, reason);
-                                               (chan, MsgHandleErrInternal::from_finish_shutdown(msg, channel_id, shutdown_res, None))
-                                       } else { unreachable!(); });
+                                               let shutdown_res = $chan.context.force_shutdown(false, reason);
+                                               MsgHandleErrInternal::from_finish_shutdown(msg, channel_id, shutdown_res, None)
+                                       } else { unreachable!(); };
+
+                                       mem::drop(peer_state_lock);
+                                       mem::drop(per_peer_state);
+                                       let _: Result<(), _> = handle_error!(self, Err(err), counterparty);
+                                       Err($api_err)
+                               } } }
+                               match find_funding_output(&chan, &funding_transaction) {
+                                       Ok(found_funding_txo) => funding_txo = found_funding_txo,
+                                       Err(err) => {
+                                               let chan_err = ChannelError::Close(err.to_owned());
+                                               let api_err = APIError::APIMisuseError { err: err.to_owned() };
+                                               return close_chan!(chan_err, api_err, chan);
+                                       },
+                               }
+
+                               let logger = WithChannelContext::from(&self.logger, &chan.context, None);
+                               let funding_res = chan.get_funding_created(funding_transaction, funding_txo, is_batch_funding, &&logger);
                                match funding_res {
                                        Ok(funding_msg) => (chan, funding_msg),
-                                       Err((chan, err)) => {
-                                               mem::drop(peer_state_lock);
-                                               mem::drop(per_peer_state);
-                                               let _: Result<(), _> = handle_error!(self, Err(err), chan.context.get_counterparty_node_id());
-                                               return Err(APIError::ChannelUnavailable {
-                                                       err: "Signer refused to sign the initial commitment transaction".to_owned()
-                                               });
-                                       },
+                                       Err((mut chan, chan_err)) => {
+                                               let api_err = APIError::ChannelUnavailable { err: "Signer refused to sign the initial commitment transaction".to_owned() };
+                                               return close_chan!(chan_err, api_err, chan);
+                                       }
                                }
                        },
                        Some(phase) => {
@@ -3931,7 +4635,7 @@ where
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                let mut result = Ok(());
 
-               if !funding_transaction.is_coin_base() {
+               if !funding_transaction.is_coinbase() {
                        for inp in funding_transaction.input.iter() {
                                if inp.witness.is_empty() {
                                        result = result.and(Err(APIError::APIMisuseError {
@@ -3987,21 +4691,17 @@ where
                                is_batch_funding,
                                |chan, tx| {
                                        let mut output_index = None;
-                                       let expected_spk = chan.context.get_funding_redeemscript().to_v0_p2wsh();
+                                       let expected_spk = chan.context.get_funding_redeemscript().to_p2wsh();
                                        for (idx, outp) in tx.output.iter().enumerate() {
-                                               if outp.script_pubkey == expected_spk && outp.value == chan.context.get_value_satoshis() {
+                                               if outp.script_pubkey == expected_spk && outp.value.to_sat() == chan.context.get_value_satoshis() {
                                                        if output_index.is_some() {
-                                                               return Err(APIError::APIMisuseError {
-                                                                       err: "Multiple outputs matched the expected script and value".to_owned()
-                                                               });
+                                                               return Err("Multiple outputs matched the expected script and value");
                                                        }
                                                        output_index = Some(idx as u16);
                                                }
                                        }
                                        if output_index.is_none() {
-                                               return Err(APIError::APIMisuseError {
-                                                       err: "No output matched the script_pubkey and value in the FundingGenerationReady event".to_owned()
-                                               });
+                                               return Err("No output matched the script_pubkey and value in the FundingGenerationReady event");
                                        }
                                        let outpoint = OutPoint { txid: tx.txid(), index: output_index.unwrap() };
                                        if let Some(funding_batch_state) = funding_batch_state.as_mut() {
@@ -4032,11 +4732,20 @@ where
                                for (channel_id, counterparty_node_id) in channels_to_remove {
                                        per_peer_state.get(&counterparty_node_id)
                                                .map(|peer_state_mutex| peer_state_mutex.lock().unwrap())
-                                               .and_then(|mut peer_state| peer_state.channel_by_id.remove(&channel_id))
-                                               .map(|mut chan| {
+                                               .and_then(|mut peer_state| peer_state.channel_by_id.remove(&channel_id).map(|chan| (chan, peer_state)))
+                                               .map(|(mut chan, mut peer_state)| {
                                                        update_maps_on_chan_removal!(self, &chan.context());
                                                        let closure_reason = ClosureReason::ProcessingError { err: e.clone() };
                                                        shutdown_results.push(chan.context_mut().force_shutdown(false, closure_reason));
+                                                       peer_state.pending_msg_events.push(events::MessageSendEvent::HandleError {
+                                                               node_id: counterparty_node_id,
+                                                               action: msgs::ErrorAction::SendErrorMessage {
+                                                                       msg: msgs::ErrorMessage {
+                                                                               channel_id,
+                                                                               data: "Failed to fund channel".to_owned(),
+                                                                       }
+                                                               },
+                                                       });
                                                });
                                }
                        }
@@ -4085,6 +4794,7 @@ where
                        .ok_or_else(|| APIError::ChannelUnavailable { err: format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id) })?;
                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                let peer_state = &mut *peer_state_lock;
+
                for channel_id in channel_ids {
                        if !peer_state.has_channel(channel_id) {
                                return Err(APIError::ChannelUnavailable {
@@ -4101,7 +4811,8 @@ where
                                }
                                if let ChannelPhase::Funded(channel) = channel_phase {
                                        if let Ok(msg) = self.get_channel_update_for_broadcast(channel) {
-                                               peer_state.pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate { msg });
+                                               let mut pending_broadcast_messages = self.pending_broadcast_messages.lock().unwrap();
+                                               pending_broadcast_messages.push(events::MessageSendEvent::BroadcastChannelUpdate { msg });
                                        } else if let Ok(msg) = self.get_channel_update_for_unicast(channel) {
                                                peer_state.pending_msg_events.push(events::MessageSendEvent::SendChannelUpdate {
                                                        node_id: channel.context.get_counterparty_node_id(),
@@ -4201,7 +4912,7 @@ where
                                None => {
                                        let error = format!("Channel with id {} not found for the passed counterparty node_id {}",
                                                next_hop_channel_id, next_node_id);
-                                       let logger = WithContext::from(&self.logger, Some(next_node_id), Some(*next_hop_channel_id));
+                                       let logger = WithContext::from(&self.logger, Some(next_node_id), Some(*next_hop_channel_id), None);
                                        log_error!(logger, "{} when attempting to forward intercepted HTLC", error);
                                        return Err(APIError::ChannelUnavailable {
                                                err: error
@@ -4276,6 +4987,145 @@ where
                Ok(())
        }
 
+       fn process_pending_update_add_htlcs(&self) {
+               let mut decode_update_add_htlcs = new_hash_map();
+               mem::swap(&mut decode_update_add_htlcs, &mut self.decode_update_add_htlcs.lock().unwrap());
+
+               let get_failed_htlc_destination = |outgoing_scid_opt: Option<u64>, payment_hash: PaymentHash| {
+                       if let Some(outgoing_scid) = outgoing_scid_opt {
+                               match self.short_to_chan_info.read().unwrap().get(&outgoing_scid) {
+                                       Some((outgoing_counterparty_node_id, outgoing_channel_id)) =>
+                                               HTLCDestination::NextHopChannel {
+                                                       node_id: Some(*outgoing_counterparty_node_id),
+                                                       channel_id: *outgoing_channel_id,
+                                               },
+                                       None => HTLCDestination::UnknownNextHop {
+                                               requested_forward_scid: outgoing_scid,
+                                       },
+                               }
+                       } else {
+                               HTLCDestination::FailedPayment { payment_hash }
+                       }
+               };
+
+               'outer_loop: for (incoming_scid, update_add_htlcs) in decode_update_add_htlcs {
+                       let incoming_channel_details_opt = self.do_funded_channel_callback(incoming_scid, |chan: &mut Channel<SP>| {
+                               let counterparty_node_id = chan.context.get_counterparty_node_id();
+                               let channel_id = chan.context.channel_id();
+                               let funding_txo = chan.context.get_funding_txo().unwrap();
+                               let user_channel_id = chan.context.get_user_id();
+                               let accept_underpaying_htlcs = chan.context.config().accept_underpaying_htlcs;
+                               (counterparty_node_id, channel_id, funding_txo, user_channel_id, accept_underpaying_htlcs)
+                       });
+                       let (
+                               incoming_counterparty_node_id, incoming_channel_id, incoming_funding_txo,
+                               incoming_user_channel_id, incoming_accept_underpaying_htlcs
+                        ) = if let Some(incoming_channel_details) = incoming_channel_details_opt {
+                               incoming_channel_details
+                       } else {
+                               // The incoming channel no longer exists, HTLCs should be resolved onchain instead.
+                               continue;
+                       };
+
+                       let mut htlc_forwards = Vec::new();
+                       let mut htlc_fails = Vec::new();
+                       for update_add_htlc in &update_add_htlcs {
+                               let (next_hop, shared_secret, next_packet_details_opt) = match decode_incoming_update_add_htlc_onion(
+                                       &update_add_htlc, &self.node_signer, &self.logger, &self.secp_ctx
+                               ) {
+                                       Ok(decoded_onion) => decoded_onion,
+                                       Err(htlc_fail) => {
+                                               htlc_fails.push((htlc_fail, HTLCDestination::InvalidOnion));
+                                               continue;
+                                       },
+                               };
+
+                               let is_intro_node_blinded_forward = next_hop.is_intro_node_blinded_forward();
+                               let outgoing_scid_opt = next_packet_details_opt.as_ref().map(|d| d.outgoing_scid);
+
+                               // Process the HTLC on the incoming channel.
+                               match self.do_funded_channel_callback(incoming_scid, |chan: &mut Channel<SP>| {
+                                       let logger = WithChannelContext::from(&self.logger, &chan.context, Some(update_add_htlc.payment_hash));
+                                       chan.can_accept_incoming_htlc(
+                                               update_add_htlc, &self.fee_estimator, &logger,
+                                       )
+                               }) {
+                                       Some(Ok(_)) => {},
+                                       Some(Err((err, code))) => {
+                                               let outgoing_chan_update_opt = if let Some(outgoing_scid) = outgoing_scid_opt.as_ref() {
+                                                       self.do_funded_channel_callback(*outgoing_scid, |chan: &mut Channel<SP>| {
+                                                               self.get_channel_update_for_onion(*outgoing_scid, chan).ok()
+                                                       }).flatten()
+                                               } else {
+                                                       None
+                                               };
+                                               let htlc_fail = self.htlc_failure_from_update_add_err(
+                                                       &update_add_htlc, &incoming_counterparty_node_id, err, code,
+                                                       outgoing_chan_update_opt, is_intro_node_blinded_forward, &shared_secret,
+                                               );
+                                               let htlc_destination = get_failed_htlc_destination(outgoing_scid_opt, update_add_htlc.payment_hash);
+                                               htlc_fails.push((htlc_fail, htlc_destination));
+                                               continue;
+                                       },
+                                       // The incoming channel no longer exists, HTLCs should be resolved onchain instead.
+                                       None => continue 'outer_loop,
+                               }
+
+                               // Now process the HTLC on the outgoing channel if it's a forward.
+                               if let Some(next_packet_details) = next_packet_details_opt.as_ref() {
+                                       if let Err((err, code, chan_update_opt)) = self.can_forward_htlc(
+                                               &update_add_htlc, next_packet_details
+                                       ) {
+                                               let htlc_fail = self.htlc_failure_from_update_add_err(
+                                                       &update_add_htlc, &incoming_counterparty_node_id, err, code,
+                                                       chan_update_opt, is_intro_node_blinded_forward, &shared_secret,
+                                               );
+                                               let htlc_destination = get_failed_htlc_destination(outgoing_scid_opt, update_add_htlc.payment_hash);
+                                               htlc_fails.push((htlc_fail, htlc_destination));
+                                               continue;
+                                       }
+                               }
+
+                               match self.construct_pending_htlc_status(
+                                       &update_add_htlc, &incoming_counterparty_node_id, shared_secret, next_hop,
+                                       incoming_accept_underpaying_htlcs, next_packet_details_opt.map(|d| d.next_packet_pubkey),
+                               ) {
+                                       PendingHTLCStatus::Forward(htlc_forward) => {
+                                               htlc_forwards.push((htlc_forward, update_add_htlc.htlc_id));
+                                       },
+                                       PendingHTLCStatus::Fail(htlc_fail) => {
+                                               let htlc_destination = get_failed_htlc_destination(outgoing_scid_opt, update_add_htlc.payment_hash);
+                                               htlc_fails.push((htlc_fail, htlc_destination));
+                                       },
+                               }
+                       }
+
+                       // Process all of the forwards and failures for the channel in which the HTLCs were
+                       // proposed to as a batch.
+                       let pending_forwards = (incoming_scid, incoming_funding_txo, incoming_channel_id,
+                               incoming_user_channel_id, htlc_forwards.drain(..).collect());
+                       self.forward_htlcs_without_forward_event(&mut [pending_forwards]);
+                       for (htlc_fail, htlc_destination) in htlc_fails.drain(..) {
+                               let failure = match htlc_fail {
+                                       HTLCFailureMsg::Relay(fail_htlc) => HTLCForwardInfo::FailHTLC {
+                                               htlc_id: fail_htlc.htlc_id,
+                                               err_packet: fail_htlc.reason,
+                                       },
+                                       HTLCFailureMsg::Malformed(fail_malformed_htlc) => HTLCForwardInfo::FailMalformedHTLC {
+                                               htlc_id: fail_malformed_htlc.htlc_id,
+                                               sha256_of_onion: fail_malformed_htlc.sha256_of_onion,
+                                               failure_code: fail_malformed_htlc.failure_code,
+                                       },
+                               };
+                               self.forward_htlcs.lock().unwrap().entry(incoming_scid).or_insert(vec![]).push(failure);
+                               self.pending_events.lock().unwrap().push_back((events::Event::HTLCHandlingFailed {
+                                       prev_channel_id: incoming_channel_id,
+                                       failed_next_destination: htlc_destination,
+                               }, None));
+                       }
+               }
+       }
+
        /// Processes HTLCs which are pending waiting on random forward delay.
        ///
        /// Should only really ever be called in response to a PendingHTLCsForwardable event.
@@ -4283,6 +5133,8 @@ where
        pub fn process_pending_htlc_forwards(&self) {
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
 
+               self.process_pending_update_add_htlcs();
+
                let mut new_events = VecDeque::new();
                let mut failed_forwards = Vec::new();
                let mut phantom_receives: Vec<(u64, OutPoint, ChannelId, u128, Vec<(PendingHTLCInfo, u64)>)> = Vec::new();
@@ -4306,7 +5158,7 @@ where
                                                                        }) => {
                                                                                macro_rules! failure_handler {
                                                                                        ($msg: expr, $err_code: expr, $err_data: expr, $phantom_ss: expr, $next_hop_unknown: expr) => {
-                                                                                               let logger = WithContext::from(&self.logger, forwarding_counterparty, Some(prev_channel_id));
+                                                                                               let logger = WithContext::from(&self.logger, forwarding_counterparty, Some(prev_channel_id), Some(payment_hash));
                                                                                                log_info!(logger, "Failed to accept/forward incoming HTLC: {}", $msg);
 
                                                                                                let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
@@ -4417,7 +5269,7 @@ where
                                        let mut peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
                                        let peer_state = &mut *peer_state_lock;
                                        if let Some(ChannelPhase::Funded(ref mut chan)) = peer_state.channel_by_id.get_mut(&forward_chan_id) {
-                                               let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                               let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                                for forward_info in pending_forwards.drain(..) {
                                                        let queue_fail_htlc_res = match forward_info {
                                                                HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
@@ -4429,6 +5281,7 @@ where
                                                                                }, skimmed_fee_msat, ..
                                                                        },
                                                                }) => {
+                                                                       let logger = WithChannelContext::from(&self.logger, &chan.context, Some(payment_hash));
                                                                        log_trace!(logger, "Adding HTLC from short id {} with payment_hash {} to channel with short id {} after delay", prev_short_channel_id, &payment_hash, short_chan_id);
                                                                        let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
                                                                                short_channel_id: prev_short_channel_id,
@@ -4512,25 +5365,29 @@ where
                                                                }
                                                        }) => {
                                                                let blinded_failure = routing.blinded_failure();
-                                                               let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret, mut onion_fields) = match routing {
+                                                               let (cltv_expiry, onion_payload, payment_data, payment_context, phantom_shared_secret, mut onion_fields) = match routing {
                                                                        PendingHTLCRouting::Receive {
-                                                                               payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret,
-                                                                               custom_tlvs, requires_blinded_error: _
+                                                                               payment_data, payment_metadata, payment_context,
+                                                                               incoming_cltv_expiry, phantom_shared_secret, custom_tlvs,
+                                                                               requires_blinded_error: _
                                                                        } => {
                                                                                let _legacy_hop_data = Some(payment_data.clone());
                                                                                let onion_fields = RecipientOnionFields { payment_secret: Some(payment_data.payment_secret),
                                                                                                payment_metadata, custom_tlvs };
                                                                                (incoming_cltv_expiry, OnionPayload::Invoice { _legacy_hop_data },
-                                                                                       Some(payment_data), phantom_shared_secret, onion_fields)
+                                                                                       Some(payment_data), payment_context, phantom_shared_secret, onion_fields)
                                                                        },
-                                                                       PendingHTLCRouting::ReceiveKeysend { payment_data, payment_preimage, payment_metadata, incoming_cltv_expiry, custom_tlvs } => {
+                                                                       PendingHTLCRouting::ReceiveKeysend {
+                                                                               payment_data, payment_preimage, payment_metadata,
+                                                                               incoming_cltv_expiry, custom_tlvs, requires_blinded_error: _
+                                                                       } => {
                                                                                let onion_fields = RecipientOnionFields {
                                                                                        payment_secret: payment_data.as_ref().map(|data| data.payment_secret),
                                                                                        payment_metadata,
                                                                                        custom_tlvs,
                                                                                };
                                                                                (incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage),
-                                                                                       payment_data, None, onion_fields)
+                                                                                       payment_data, None, None, onion_fields)
                                                                        },
                                                                        _ => {
                                                                                panic!("short_channel_id == 0 should imply any pending_forward entries are of type Receive");
@@ -4595,10 +5452,7 @@ where
                                                                macro_rules! check_total_value {
                                                                        ($purpose: expr) => {{
                                                                                let mut payment_claimable_generated = false;
-                                                                               let is_keysend = match $purpose {
-                                                                                       events::PaymentPurpose::SpontaneousPayment(_) => true,
-                                                                                       events::PaymentPurpose::InvoicePayment { .. } => false,
-                                                                               };
+                                                                               let is_keysend = $purpose.is_keysend();
                                                                                let mut claimable_payments = self.claimable_payments.lock().unwrap();
                                                                                if claimable_payments.pending_claiming_payments.contains_key(&payment_hash) {
                                                                                        fail_htlc!(claimable_htlc, payment_hash);
@@ -4712,10 +5566,11 @@ where
                                                                                                                fail_htlc!(claimable_htlc, payment_hash);
                                                                                                        }
                                                                                                }
-                                                                                               let purpose = events::PaymentPurpose::InvoicePayment {
-                                                                                                       payment_preimage: payment_preimage.clone(),
-                                                                                                       payment_secret: payment_data.payment_secret,
-                                                                                               };
+                                                                                               let purpose = events::PaymentPurpose::from_parts(
+                                                                                                       payment_preimage,
+                                                                                                       payment_data.payment_secret,
+                                                                                                       payment_context,
+                                                                                               );
                                                                                                check_total_value!(purpose);
                                                                                        },
                                                                                        OnionPayload::Spontaneous(preimage) => {
@@ -4738,10 +5593,11 @@ where
                                                                                                &payment_hash, payment_data.total_msat, inbound_payment.get().min_value_msat.unwrap());
                                                                                        fail_htlc!(claimable_htlc, payment_hash);
                                                                                } else {
-                                                                                       let purpose = events::PaymentPurpose::InvoicePayment {
-                                                                                               payment_preimage: inbound_payment.get().payment_preimage,
-                                                                                               payment_secret: payment_data.payment_secret,
-                                                                                       };
+                                                                                       let purpose = events::PaymentPurpose::from_parts(
+                                                                                               inbound_payment.get().payment_preimage,
+                                                                                               payment_data.payment_secret,
+                                                                                               payment_context,
+                                                                                       );
                                                                                        let payment_claimable_generated = check_total_value!(purpose);
                                                                                        if payment_claimable_generated {
                                                                                                inbound_payment.remove_entry();
@@ -4858,7 +5714,7 @@ where
        fn update_channel_fee(&self, chan_id: &ChannelId, chan: &mut Channel<SP>, new_feerate: u32) -> NotifyOption {
                if !chan.context.is_outbound() { return NotifyOption::SkipPersistNoEvents; }
 
-               let logger = WithChannelContext::from(&self.logger, &chan.context);
+               let logger = WithChannelContext::from(&self.logger, &chan.context, None);
 
                // If the feerate has decreased by less than half, don't bother
                if new_feerate <= chan.context.get_feerate_sat_per_1000_weight() && new_feerate * 2 > chan.context.get_feerate_sat_per_1000_weight() {
@@ -4951,7 +5807,7 @@ where
                        | {
                                context.maybe_expire_prev_config();
                                if unfunded_context.should_expire_unfunded_channel() {
-                                       let logger = WithChannelContext::from(&self.logger, context);
+                                       let logger = WithChannelContext::from(&self.logger, context, None);
                                        log_error!(logger,
                                                "Force-closing pending channel with ID {} for not establishing in a timely manner", chan_id);
                                        update_maps_on_chan_removal!(self, &context);
@@ -5007,7 +5863,8 @@ where
                                                                                if n >= DISABLE_GOSSIP_TICKS {
                                                                                        chan.set_channel_update_status(ChannelUpdateStatus::Disabled);
                                                                                        if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
-                                                                                               pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
+                                                                                               let mut pending_broadcast_messages = self.pending_broadcast_messages.lock().unwrap();
+                                                                                               pending_broadcast_messages.push(events::MessageSendEvent::BroadcastChannelUpdate {
                                                                                                        msg: update
                                                                                                });
                                                                                        }
@@ -5021,7 +5878,8 @@ where
                                                                                if n >= ENABLE_GOSSIP_TICKS {
                                                                                        chan.set_channel_update_status(ChannelUpdateStatus::Enabled);
                                                                                        if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
-                                                                                               pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
+                                                                                               let mut pending_broadcast_messages = self.pending_broadcast_messages.lock().unwrap();
+                                                                                               pending_broadcast_messages.push(events::MessageSendEvent::BroadcastChannelUpdate {
                                                                                                        msg: update
                                                                                                });
                                                                                        }
@@ -5036,7 +5894,7 @@ where
                                                                chan.context.maybe_expire_prev_config();
 
                                                                if chan.should_disconnect_peer_awaiting_response() {
-                                                                       let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                                                       let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                                                        log_debug!(logger, "Disconnecting peer {} due to not making any progress on channel {}",
                                                                                        counterparty_node_id, chan_id);
                                                                        pending_msg_events.push(MessageSendEvent::HandleError {
@@ -5060,12 +5918,12 @@ where
                                                                process_unfunded_channel_tick(chan_id, &mut chan.context, &mut chan.unfunded_context,
                                                                        pending_msg_events, counterparty_node_id)
                                                        },
-                                                       #[cfg(dual_funding)]
+                                                       #[cfg(any(dual_funding, splicing))]
                                                        ChannelPhase::UnfundedInboundV2(chan) => {
                                                                process_unfunded_channel_tick(chan_id, &mut chan.context, &mut chan.unfunded_context,
                                                                        pending_msg_events, counterparty_node_id)
                                                        },
-                                                       #[cfg(dual_funding)]
+                                                       #[cfg(any(dual_funding, splicing))]
                                                        ChannelPhase::UnfundedOutboundV2(chan) => {
                                                                process_unfunded_channel_tick(chan_id, &mut chan.context, &mut chan.unfunded_context,
                                                                        pending_msg_events, counterparty_node_id)
@@ -5075,7 +5933,7 @@ where
 
                                        for (chan_id, req) in peer_state.inbound_channel_request_by_id.iter_mut() {
                                                if { req.ticks_remaining -= 1 ; req.ticks_remaining } <= 0 {
-                                                       let logger = WithContext::from(&self.logger, Some(counterparty_node_id), Some(*chan_id));
+                                                       let logger = WithContext::from(&self.logger, Some(counterparty_node_id), Some(*chan_id), None);
                                                        log_error!(logger, "Force-closing unaccepted inbound channel {} for not accepting in a timely manner", &chan_id);
                                                        peer_state.pending_msg_events.push(
                                                                events::MessageSendEvent::HandleError {
@@ -5324,9 +6182,14 @@ where
                }
        }
 
+       fn fail_htlc_backwards_internal(&self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason, destination: HTLCDestination) {
+               let push_forward_event = self.fail_htlc_backwards_internal_without_forward_event(source, payment_hash, onion_error, destination);
+               if push_forward_event { self.push_pending_forwards_ev(); }
+       }
+
        /// Fails an HTLC backwards to the sender of it to us.
        /// Note that we do not assume that channels corresponding to failed HTLCs are still available.
-       fn fail_htlc_backwards_internal(&self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason, destination: HTLCDestination) {
+       fn fail_htlc_backwards_internal_without_forward_event(&self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason, destination: HTLCDestination) -> bool {
                // Ensure that no peer state channel storage lock is held when calling this function.
                // This ensures that future code doesn't introduce a lock-order requirement for
                // `forward_htlcs` to be locked after the `per_peer_state` peer locks, which calling
@@ -5344,19 +6207,19 @@ where
                // Note that we MUST NOT end up calling methods on self.chain_monitor here - we're called
                // from block_connected which may run during initialization prior to the chain_monitor
                // being fully configured. See the docs for `ChannelManagerReadArgs` for more.
+               let mut push_forward_event;
                match source {
                        HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, .. } => {
-                               if self.pending_outbound_payments.fail_htlc(source, payment_hash, onion_error, path,
+                               push_forward_event = self.pending_outbound_payments.fail_htlc(source, payment_hash, onion_error, path,
                                        session_priv, payment_id, self.probing_cookie_secret, &self.secp_ctx,
-                                       &self.pending_events, &self.logger)
-                               { self.push_pending_forwards_ev(); }
+                                       &self.pending_events, &self.logger);
                        },
                        HTLCSource::PreviousHopData(HTLCPreviousHopData {
                                ref short_channel_id, ref htlc_id, ref incoming_packet_shared_secret,
                                ref phantom_shared_secret, outpoint: _, ref blinded_failure, ref channel_id, ..
                        }) => {
                                log_trace!(
-                                       WithContext::from(&self.logger, None, Some(*channel_id)),
+                                       WithContext::from(&self.logger, None, Some(*channel_id), Some(*payment_hash)),
                                        "Failing {}HTLC with payment_hash {} backwards from us: {:?}",
                                        if blinded_failure.is_some() { "blinded " } else { "" }, &payment_hash, onion_error
                                );
@@ -5383,11 +6246,9 @@ where
                                        }
                                };
 
-                               let mut push_forward_ev = false;
+                               push_forward_event = self.decode_update_add_htlcs.lock().unwrap().is_empty();
                                let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
-                               if forward_htlcs.is_empty() {
-                                       push_forward_ev = true;
-                               }
+                               push_forward_event &= forward_htlcs.is_empty();
                                match forward_htlcs.entry(*short_channel_id) {
                                        hash_map::Entry::Occupied(mut entry) => {
                                                entry.get_mut().push(failure);
@@ -5397,7 +6258,6 @@ where
                                        }
                                }
                                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_back((events::Event::HTLCHandlingFailed {
                                        prev_channel_id: *channel_id,
@@ -5405,6 +6265,7 @@ where
                                }, None));
                        },
                }
+               push_forward_event
        }
 
        /// Provides a payment preimage in response to [`Event::PaymentClaimable`], generating any
@@ -5466,19 +6327,27 @@ where
                                        }
                                }
 
-                               let htlcs = payment.htlcs.iter().map(events::ClaimedHTLC::from).collect();
-                               let sender_intended_value = payment.htlcs.first().map(|htlc| htlc.total_msat);
-                               let dup_purpose = claimable_payments.pending_claiming_payments.insert(payment_hash,
-                                       ClaimingPayment { amount_msat: payment.htlcs.iter().map(|source| source.value).sum(),
-                                       payment_purpose: payment.purpose, receiver_node_id, htlcs, sender_intended_value
-                               });
-                               if dup_purpose.is_some() {
-                                       debug_assert!(false, "Shouldn't get a duplicate pending claim event ever");
-                                       log_error!(self.logger, "Got a duplicate pending claimable event on payment hash {}! Please report this bug",
-                                               &payment_hash);
-                               }
+                               let claiming_payment = claimable_payments.pending_claiming_payments
+                                       .entry(payment_hash)
+                                       .and_modify(|_| {
+                                               debug_assert!(false, "Shouldn't get a duplicate pending claim event ever");
+                                               log_error!(self.logger, "Got a duplicate pending claimable event on payment hash {}! Please report this bug",
+                                                       &payment_hash);
+                                       })
+                                       .or_insert_with(|| {
+                                               let htlcs = payment.htlcs.iter().map(events::ClaimedHTLC::from).collect();
+                                               let sender_intended_value = payment.htlcs.first().map(|htlc| htlc.total_msat);
+                                               ClaimingPayment {
+                                                       amount_msat: payment.htlcs.iter().map(|source| source.value).sum(),
+                                                       payment_purpose: payment.purpose,
+                                                       receiver_node_id,
+                                                       htlcs,
+                                                       sender_intended_value,
+                                                       onion_fields: payment.onion_fields,
+                                               }
+                                       });
 
-                               if let Some(RecipientOnionFields { ref custom_tlvs, .. }) = payment.onion_fields {
+                               if let Some(RecipientOnionFields { ref custom_tlvs, .. }) = claiming_payment.onion_fields {
                                        if !custom_tlvs_known && custom_tlvs.iter().any(|(typ, _)| typ % 2 == 0) {
                                                log_info!(self.logger, "Rejecting payment with payment hash {} as we cannot accept payment with unknown even TLVs: {}",
                                                        &payment_hash, log_iter!(custom_tlvs.iter().map(|(typ, _)| typ).filter(|typ| *typ % 2 == 0)));
@@ -5552,7 +6421,7 @@ where
                                        if let msgs::ErrorAction::IgnoreError = err.err.action {
                                                // We got a temporary failure updating monitor, but will claim the
                                                // HTLC when the monitor updating is restored (or on chain).
-                                               let logger = WithContext::from(&self.logger, None, Some(prev_hop_chan_id));
+                                               let logger = WithContext::from(&self.logger, None, Some(prev_hop_chan_id), Some(payment_hash));
                                                log_error!(logger, "Temporary failure claiming HTLC, treating as success: {}", err.err.err);
                                        } else { errs.push((pk, err)); }
                                }
@@ -5611,7 +6480,7 @@ where
                                if let hash_map::Entry::Occupied(mut chan_phase_entry) = peer_state.channel_by_id.entry(chan_id) {
                                        if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
                                                let counterparty_node_id = chan.context.get_counterparty_node_id();
-                                               let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                               let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                                let fulfill_res = chan.get_update_fulfill_htlc_and_commit(prev_hop.htlc_id, payment_preimage, &&logger);
 
                                                match fulfill_res {
@@ -5704,7 +6573,7 @@ where
                                // with a preimage we *must* somehow manage to propagate it to the upstream
                                // channel, or we must have an ability to receive the same event and try
                                // again on restart.
-                               log_error!(WithContext::from(&self.logger, None, Some(prev_hop.channel_id)),
+                               log_error!(WithContext::from(&self.logger, None, Some(prev_hop.channel_id), None),
                                        "Critical error: failed to update channel monitor with preimage {:?}: {:?}",
                                        payment_preimage, update_res);
                        }
@@ -5740,7 +6609,7 @@ where
        fn claim_funds_internal(&self, source: HTLCSource, payment_preimage: PaymentPreimage,
                forwarded_htlc_value_msat: Option<u64>, skimmed_fee_msat: Option<u64>, from_onchain: bool,
                startup_replay: bool, next_channel_counterparty_node_id: Option<PublicKey>,
-               next_channel_outpoint: OutPoint, next_channel_id: ChannelId,
+               next_channel_outpoint: OutPoint, next_channel_id: ChannelId, next_user_channel_id: Option<u128>,
        ) {
                match source {
                        HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
@@ -5759,11 +6628,10 @@ where
                        },
                        HTLCSource::PreviousHopData(hop_data) => {
                                let prev_channel_id = hop_data.channel_id;
+                               let prev_user_channel_id = hop_data.user_channel_id;
                                let completed_blocker = RAAMonitorUpdateBlockingAction::from_prev_hop_data(&hop_data);
                                #[cfg(debug_assertions)]
                                let claiming_chan_funding_outpoint = hop_data.outpoint;
-                               #[cfg(debug_assertions)]
-                               let claiming_channel_id = hop_data.channel_id;
                                let res = self.claim_funds_from_hop(hop_data, payment_preimage,
                                        |htlc_claim_value_msat, definitely_duplicate| {
                                                let chan_to_release =
@@ -5821,7 +6689,7 @@ where
                                                                                BackgroundEvent::MonitorUpdatesComplete {
                                                                                        channel_id, ..
                                                                                } =>
-                                                                                       *channel_id == claiming_channel_id,
+                                                                                       *channel_id == prev_channel_id,
                                                                        }
                                                                }), "{:?}", *background_events);
                                                        }
@@ -5845,12 +6713,14 @@ where
                                                                "skimmed_fee_msat must always be included in total_fee_earned_msat");
                                                        Some(MonitorUpdateCompletionAction::EmitEventAndFreeOtherChannel {
                                                                event: events::Event::PaymentForwarded {
-                                                                       total_fee_earned_msat,
-                                                                       claim_from_onchain_tx: from_onchain,
                                                                        prev_channel_id: Some(prev_channel_id),
                                                                        next_channel_id: Some(next_channel_id),
-                                                                       outbound_amount_forwarded_msat: forwarded_htlc_value_msat,
+                                                                       prev_user_channel_id,
+                                                                       next_user_channel_id,
+                                                                       total_fee_earned_msat,
                                                                        skimmed_fee_msat,
+                                                                       claim_from_onchain_tx: from_onchain,
+                                                                       outbound_amount_forwarded_msat: forwarded_htlc_value_msat,
                                                                },
                                                                downstream_counterparty_and_funding_outpoint: chan_to_release,
                                                        })
@@ -5884,6 +6754,7 @@ where
                                                receiver_node_id,
                                                htlcs,
                                                sender_intended_value: sender_intended_total_msat,
+                                               onion_fields,
                                        }) = payment {
                                                self.pending_events.lock().unwrap().push_back((events::Event::PaymentClaimed {
                                                        payment_hash,
@@ -5892,6 +6763,7 @@ where
                                                        receiver_node_id: Some(receiver_node_id),
                                                        htlcs,
                                                        sender_intended_total_msat,
+                                                       onion_fields,
                                                }, None));
                                        }
                                },
@@ -5922,24 +6794,31 @@ where
        fn handle_channel_resumption(&self, pending_msg_events: &mut Vec<MessageSendEvent>,
                channel: &mut Channel<SP>, raa: Option<msgs::RevokeAndACK>,
                commitment_update: Option<msgs::CommitmentUpdate>, order: RAACommitmentOrder,
-               pending_forwards: Vec<(PendingHTLCInfo, u64)>, funding_broadcastable: Option<Transaction>,
+               pending_forwards: Vec<(PendingHTLCInfo, u64)>, pending_update_adds: Vec<msgs::UpdateAddHTLC>,
+               funding_broadcastable: Option<Transaction>,
                channel_ready: Option<msgs::ChannelReady>, announcement_sigs: Option<msgs::AnnouncementSignatures>)
-       -> Option<(u64, OutPoint, ChannelId, u128, Vec<(PendingHTLCInfo, u64)>)> {
-               let logger = WithChannelContext::from(&self.logger, &channel.context);
-               log_trace!(logger, "Handling channel resumption for channel {} with {} RAA, {} commitment update, {} pending forwards, {}broadcasting funding, {} channel ready, {} announcement",
+       -> (Option<(u64, OutPoint, ChannelId, u128, Vec<(PendingHTLCInfo, u64)>)>, Option<(u64, Vec<msgs::UpdateAddHTLC>)>) {
+               let logger = WithChannelContext::from(&self.logger, &channel.context, None);
+               log_trace!(logger, "Handling channel resumption for channel {} with {} RAA, {} commitment update, {} pending forwards, {} pending update_add_htlcs, {}broadcasting funding, {} channel ready, {} announcement",
                        &channel.context.channel_id(),
                        if raa.is_some() { "an" } else { "no" },
-                       if commitment_update.is_some() { "a" } else { "no" }, pending_forwards.len(),
+                       if commitment_update.is_some() { "a" } else { "no" },
+                       pending_forwards.len(), pending_update_adds.len(),
                        if funding_broadcastable.is_some() { "" } else { "not " },
                        if channel_ready.is_some() { "sending" } else { "without" },
                        if announcement_sigs.is_some() { "sending" } else { "without" });
 
-               let mut htlc_forwards = None;
-
                let counterparty_node_id = channel.context.get_counterparty_node_id();
+               let short_channel_id = channel.context.get_short_channel_id().unwrap_or(channel.context.outbound_scid_alias());
+
+               let mut htlc_forwards = None;
                if !pending_forwards.is_empty() {
-                       htlc_forwards = Some((channel.context.get_short_channel_id().unwrap_or(channel.context.outbound_scid_alias()),
-                               channel.context.get_funding_txo().unwrap(), channel.context.channel_id(), channel.context.get_user_id(), pending_forwards));
+                       htlc_forwards = Some((short_channel_id, channel.context.get_funding_txo().unwrap(),
+                               channel.context.channel_id(), channel.context.get_user_id(), pending_forwards));
+               }
+               let mut decode_update_add_htlcs = None;
+               if !pending_update_adds.is_empty() {
+                       decode_update_add_htlcs = Some((short_channel_id, pending_update_adds));
                }
 
                if let Some(msg) = channel_ready {
@@ -5990,7 +6869,7 @@ where
                        emit_channel_ready_event!(pending_events, channel);
                }
 
-               htlc_forwards
+               (htlc_forwards, decode_update_add_htlcs)
        }
 
        fn channel_monitor_updated(&self, funding_txo: &OutPoint, channel_id: &ChannelId, highest_applied_update_id: u64, counterparty_node_id: Option<&PublicKey>) {
@@ -6030,7 +6909,7 @@ where
                                pending.retain(|upd| upd.update_id > highest_applied_update_id);
                                pending.len()
                        } else { 0 };
-               let logger = WithChannelContext::from(&self.logger, &channel.context);
+               let logger = WithChannelContext::from(&self.logger, &channel.context, None);
                log_trace!(logger, "ChannelMonitor updated to {}. Current highest is {}. {} pending in-flight updates.",
                        highest_applied_update_id, channel.context.get_latest_monitor_update_id(),
                        remaining_in_flight);
@@ -6084,7 +6963,7 @@ where
 
        fn do_accept_inbound_channel(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, accept_0conf: bool, user_channel_id: u128) -> Result<(), APIError> {
 
-               let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(*temporary_channel_id));
+               let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(*temporary_channel_id), None);
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
 
                let peers_without_funded_channels =
@@ -6105,73 +6984,82 @@ where
                // happening and return an error. N.B. that we create channel with an outbound SCID of zero so
                // that we can delay allocating the SCID until after we're sure that the checks below will
                // succeed.
-               let mut channel = match peer_state.inbound_channel_request_by_id.remove(temporary_channel_id) {
+               let res = match peer_state.inbound_channel_request_by_id.remove(temporary_channel_id) {
                        Some(unaccepted_channel) => {
                                let best_block_height = self.best_block.read().unwrap().height;
                                InboundV1Channel::new(&self.fee_estimator, &self.entropy_source, &self.signer_provider,
                                        counterparty_node_id.clone(), &self.channel_type_features(), &peer_state.latest_features,
                                        &unaccepted_channel.open_channel_msg, user_channel_id, &self.default_configuration, best_block_height,
-                                       &self.logger, accept_0conf).map_err(|e| {
-                                               let err_str = e.to_string();
-                                               log_error!(logger, "{}", err_str);
-
-                                               APIError::ChannelUnavailable { err: err_str }
-                                       })
-                               }
+                                       &self.logger, accept_0conf).map_err(|err| MsgHandleErrInternal::from_chan_no_close(err, *temporary_channel_id))
+                       },
                        _ => {
                                let err_str = "No such channel awaiting to be accepted.".to_owned();
                                log_error!(logger, "{}", err_str);
 
-                               Err(APIError::APIMisuseError { err: err_str })
+                               return Err(APIError::APIMisuseError { err: err_str });
                        }
-               }?;
+               };
 
-               if accept_0conf {
-                       // This should have been correctly configured by the call to InboundV1Channel::new.
-                       debug_assert!(channel.context.minimum_depth().unwrap() == 0);
-               } else if channel.context.get_channel_type().requires_zero_conf() {
-                       let send_msg_err_event = events::MessageSendEvent::HandleError {
-                               node_id: channel.context.get_counterparty_node_id(),
-                               action: msgs::ErrorAction::SendErrorMessage{
-                                       msg: msgs::ErrorMessage { channel_id: temporary_channel_id.clone(), data: "No zero confirmation channels accepted".to_owned(), }
+               match res {
+                       Err(err) => {
+                               mem::drop(peer_state_lock);
+                               mem::drop(per_peer_state);
+                               match handle_error!(self, Result::<(), MsgHandleErrInternal>::Err(err), *counterparty_node_id) {
+                                       Ok(_) => unreachable!("`handle_error` only returns Err as we've passed in an Err"),
+                                       Err(e) => {
+                                               return Err(APIError::ChannelUnavailable { err: e.err });
+                                       },
                                }
-                       };
-                       peer_state.pending_msg_events.push(send_msg_err_event);
-                       let err_str = "Please use accept_inbound_channel_from_trusted_peer_0conf to accept channels with zero confirmations.".to_owned();
-                       log_error!(logger, "{}", err_str);
+                       }
+                       Ok(mut channel) => {
+                               if accept_0conf {
+                                       // This should have been correctly configured by the call to InboundV1Channel::new.
+                                       debug_assert!(channel.context.minimum_depth().unwrap() == 0);
+                               } else if channel.context.get_channel_type().requires_zero_conf() {
+                                       let send_msg_err_event = events::MessageSendEvent::HandleError {
+                                               node_id: channel.context.get_counterparty_node_id(),
+                                               action: msgs::ErrorAction::SendErrorMessage{
+                                                       msg: msgs::ErrorMessage { channel_id: temporary_channel_id.clone(), data: "No zero confirmation channels accepted".to_owned(), }
+                                               }
+                                       };
+                                       peer_state.pending_msg_events.push(send_msg_err_event);
+                                       let err_str = "Please use accept_inbound_channel_from_trusted_peer_0conf to accept channels with zero confirmations.".to_owned();
+                                       log_error!(logger, "{}", err_str);
 
-                       return Err(APIError::APIMisuseError { err: err_str });
-               } else {
-                       // If this peer already has some channels, a new channel won't increase our number of peers
-                       // with unfunded channels, so as long as we aren't over the maximum number of unfunded
-                       // channels per-peer we can accept channels from a peer with existing ones.
-                       if is_only_peer_channel && peers_without_funded_channels >= MAX_UNFUNDED_CHANNEL_PEERS {
-                               let send_msg_err_event = events::MessageSendEvent::HandleError {
-                                       node_id: channel.context.get_counterparty_node_id(),
-                                       action: msgs::ErrorAction::SendErrorMessage{
-                                               msg: msgs::ErrorMessage { channel_id: temporary_channel_id.clone(), data: "Have too many peers with unfunded channels, not accepting new ones".to_owned(), }
-                                       }
-                               };
-                               peer_state.pending_msg_events.push(send_msg_err_event);
-                               let err_str = "Too many peers with unfunded channels, refusing to accept new ones".to_owned();
-                               log_error!(logger, "{}", err_str);
+                                       return Err(APIError::APIMisuseError { err: err_str });
+                               } else {
+                                       // If this peer already has some channels, a new channel won't increase our number of peers
+                                       // with unfunded channels, so as long as we aren't over the maximum number of unfunded
+                                       // channels per-peer we can accept channels from a peer with existing ones.
+                                       if is_only_peer_channel && peers_without_funded_channels >= MAX_UNFUNDED_CHANNEL_PEERS {
+                                               let send_msg_err_event = events::MessageSendEvent::HandleError {
+                                                       node_id: channel.context.get_counterparty_node_id(),
+                                                       action: msgs::ErrorAction::SendErrorMessage{
+                                                               msg: msgs::ErrorMessage { channel_id: temporary_channel_id.clone(), data: "Have too many peers with unfunded channels, not accepting new ones".to_owned(), }
+                                                       }
+                                               };
+                                               peer_state.pending_msg_events.push(send_msg_err_event);
+                                               let err_str = "Too many peers with unfunded channels, refusing to accept new ones".to_owned();
+                                               log_error!(logger, "{}", err_str);
 
-                               return Err(APIError::APIMisuseError { err: err_str });
-                       }
-               }
+                                               return Err(APIError::APIMisuseError { err: err_str });
+                                       }
+                               }
 
-               // Now that we know we have a channel, assign an outbound SCID alias.
-               let outbound_scid_alias = self.create_and_insert_outbound_scid_alias();
-               channel.context.set_outbound_scid_alias(outbound_scid_alias);
+                               // Now that we know we have a channel, assign an outbound SCID alias.
+                               let outbound_scid_alias = self.create_and_insert_outbound_scid_alias();
+                               channel.context.set_outbound_scid_alias(outbound_scid_alias);
 
-               peer_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel {
-                       node_id: channel.context.get_counterparty_node_id(),
-                       msg: channel.accept_inbound_channel(),
-               });
+                               peer_state.pending_msg_events.push(events::MessageSendEvent::SendAcceptChannel {
+                                       node_id: channel.context.get_counterparty_node_id(),
+                                       msg: channel.accept_inbound_channel(),
+                               });
 
-               peer_state.channel_by_id.insert(temporary_channel_id.clone(), ChannelPhase::UnfundedInboundV1(channel));
+                               peer_state.channel_by_id.insert(temporary_channel_id.clone(), ChannelPhase::UnfundedInboundV1(channel));
 
-               Ok(())
+                               Ok(())
+                       },
+               }
        }
 
        /// Gets the number of peers which match the given filter and do not have any funded, outbound,
@@ -6217,8 +7105,8 @@ where
                                                num_unfunded_channels += 1;
                                        }
                                },
-                               // TODO(dual_funding): Combine this match arm with above once #[cfg(dual_funding)] is removed.
-                               #[cfg(dual_funding)]
+                               // TODO(dual_funding): Combine this match arm with above once #[cfg(any(dual_funding, splicing))] is removed.
+                               #[cfg(any(dual_funding, splicing))]
                                ChannelPhase::UnfundedInboundV2(chan) => {
                                        // Only inbound V2 channels that are not 0conf and that we do not contribute to will be
                                        // included in the unfunded count.
@@ -6231,8 +7119,8 @@ where
                                        // Outbound channels don't contribute to the unfunded count in the DoS context.
                                        continue;
                                },
-                               // TODO(dual_funding): Combine this match arm with above once #[cfg(dual_funding)] is removed.
-                               #[cfg(dual_funding)]
+                               // TODO(dual_funding): Combine this match arm with above once #[cfg(any(dual_funding, splicing))] is removed.
+                               #[cfg(any(dual_funding, splicing))]
                                ChannelPhase::UnfundedOutboundV2(_) => {
                                        // Outbound channels don't contribute to the unfunded count in the DoS context.
                                        continue;
@@ -6375,7 +7263,7 @@ where
                                        match phase.get_mut() {
                                                ChannelPhase::UnfundedOutboundV1(chan) => {
                                                        try_chan_phase_entry!(self, chan.accept_channel(&msg, &self.default_configuration.channel_handshake_limits, &peer_state.latest_features), phase);
-                                                       (chan.context.get_value_satoshis(), chan.context.get_funding_redeemscript().to_v0_p2wsh(), chan.context.get_user_id())
+                                                       (chan.context.get_value_satoshis(), chan.context.get_funding_redeemscript().to_p2wsh(), chan.context.get_user_id())
                                                },
                                                _ => {
                                                        return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got an unexpected accept_channel message from peer with counterparty_node_id {}", counterparty_node_id), msg.common_fields.temporary_channel_id));
@@ -6411,7 +7299,7 @@ where
                let (mut chan, funding_msg_opt, monitor) =
                        match peer_state.channel_by_id.remove(&msg.temporary_channel_id) {
                                Some(ChannelPhase::UnfundedInboundV1(inbound_chan)) => {
-                                       let logger = WithChannelContext::from(&self.logger, &inbound_chan.context);
+                                       let logger = WithChannelContext::from(&self.logger, &inbound_chan.context, None);
                                        match inbound_chan.funding_created(msg, best_block, &self.signer_provider, &&logger) {
                                                Ok(res) => res,
                                                Err((inbound_chan, err)) => {
@@ -6483,7 +7371,7 @@ where
                                                        }
                                                        Ok(())
                                                } else {
-                                                       let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                                       let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                                        log_error!(logger, "Persisting initial ChannelMonitor failed, implying the funding outpoint was duplicated");
                                                        fail_chan!("Duplicate funding outpoint");
                                                }
@@ -6511,7 +7399,8 @@ where
                                        let logger = WithContext::from(
                                                &self.logger,
                                                Some(chan.context.get_counterparty_node_id()),
-                                               Some(chan.context.channel_id())
+                                               Some(chan.context.channel_id()),
+                                               None
                                        );
                                        let res =
                                                chan.funding_signed(&msg, best_block, &self.signer_provider, &&logger);
@@ -6567,7 +7456,7 @@ where
                match peer_state.channel_by_id.entry(msg.channel_id) {
                        hash_map::Entry::Occupied(mut chan_phase_entry) => {
                                if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
-                                       let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                       let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                        let announcement_sigs_opt = try_chan_phase_entry!(self, chan.channel_ready(&msg, &self.node_signer,
                                                self.chain_hash, &self.default_configuration, &self.best_block.read().unwrap(), &&logger), chan_phase_entry);
                                        if let Some(announcement_sigs) = announcement_sigs_opt {
@@ -6625,7 +7514,7 @@ where
                                match phase {
                                        ChannelPhase::Funded(chan) => {
                                                if !chan.received_shutdown() {
-                                                       let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                                       let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                                        log_info!(logger, "Received a shutdown message from our counterparty for channel {}{}.",
                                                                msg.channel_id,
                                                                if chan.sent_shutdown() { " after we initiated shutdown" } else { "" });
@@ -6653,13 +7542,13 @@ where
                                        },
                                        ChannelPhase::UnfundedInboundV1(_) | ChannelPhase::UnfundedOutboundV1(_) => {
                                                let context = phase.context_mut();
-                                               let logger = WithChannelContext::from(&self.logger, context);
+                                               let logger = WithChannelContext::from(&self.logger, context, None);
                                                log_error!(logger, "Immediately closing unfunded channel {} as peer asked to cooperatively shut it down (which is unnecessary)", &msg.channel_id);
                                                let mut chan = remove_channel_phase!(self, chan_phase_entry);
                                                finish_shutdown = Some(chan.context_mut().force_shutdown(false, ClosureReason::CounterpartyCoopClosedUnfundedChannel));
                                        },
                                        // TODO(dual_funding): Combine this match arm with above.
-                                       #[cfg(dual_funding)]
+                                       #[cfg(any(dual_funding, splicing))]
                                        ChannelPhase::UnfundedInboundV2(_) | ChannelPhase::UnfundedOutboundV2(_) => {
                                                let context = phase.context_mut();
                                                log_error!(self.logger, "Immediately closing unfunded channel {} as peer asked to cooperatively shut it down (which is unnecessary)", &msg.channel_id);
@@ -6722,14 +7611,13 @@ where
                };
                if let Some(broadcast_tx) = tx {
                        let channel_id = chan_option.as_ref().map(|channel| channel.context().channel_id());
-                       log_info!(WithContext::from(&self.logger, Some(*counterparty_node_id), channel_id), "Broadcasting {}", log_tx!(broadcast_tx));
+                       log_info!(WithContext::from(&self.logger, Some(*counterparty_node_id), channel_id, None), "Broadcasting {}", log_tx!(broadcast_tx));
                        self.tx_broadcaster.broadcast_transactions(&[&broadcast_tx]);
                }
                if let Some(ChannelPhase::Funded(chan)) = chan_option {
                        if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
-                               let mut peer_state_lock = peer_state_mutex.lock().unwrap();
-                               let peer_state = &mut *peer_state_lock;
-                               peer_state.pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
+                               let mut pending_broadcast_messages = self.pending_broadcast_messages.lock().unwrap();
+                               pending_broadcast_messages.push(events::MessageSendEvent::BroadcastChannelUpdate {
                                        msg: update
                                });
                        }
@@ -6766,7 +7654,7 @@ where
                match peer_state.channel_by_id.entry(msg.channel_id) {
                        hash_map::Entry::Occupied(mut chan_phase_entry) => {
                                if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
-                                       let pending_forward_info = match decoded_hop_res {
+                                       let mut pending_forward_info = match decoded_hop_res {
                                                Ok((next_hop, shared_secret, next_packet_pk_opt)) =>
                                                        self.construct_pending_htlc_status(
                                                                msg, counterparty_node_id, shared_secret, next_hop,
@@ -6774,44 +7662,45 @@ where
                                                        ),
                                                Err(e) => PendingHTLCStatus::Fail(e)
                                        };
-                                       let create_pending_htlc_status = |chan: &Channel<SP>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
+                                       let logger = WithChannelContext::from(&self.logger, &chan.context, Some(msg.payment_hash));
+                                       // If the update_add is completely bogus, the call will Err and we will close,
+                                       // but if we've sent a shutdown and they haven't acknowledged it yet, we just
+                                       // want to reject the new HTLC and fail it backwards instead of forwarding.
+                                       if let Err((_, error_code)) = chan.can_accept_incoming_htlc(&msg, &self.fee_estimator, &logger) {
                                                if msg.blinding_point.is_some() {
-                                                       return PendingHTLCStatus::Fail(HTLCFailureMsg::Malformed(
-                                                                       msgs::UpdateFailMalformedHTLC {
-                                                                               channel_id: msg.channel_id,
-                                                                               htlc_id: msg.htlc_id,
-                                                                               sha256_of_onion: [0; 32],
-                                                                               failure_code: INVALID_ONION_BLINDING,
-                                                                       }
-                                                       ))
-                                               }
-                                               // If the update_add is completely bogus, the call will Err and we will close,
-                                               // but if we've sent a shutdown and they haven't acknowledged it yet, we just
-                                               // want to reject the new HTLC and fail it backwards instead of forwarding.
-                                               match pending_forward_info {
-                                                       PendingHTLCStatus::Forward(PendingHTLCInfo {
-                                                               ref incoming_shared_secret, ref routing, ..
-                                                       }) => {
-                                                               let reason = if routing.blinded_failure().is_some() {
-                                                                       HTLCFailReason::reason(INVALID_ONION_BLINDING, vec![0; 32])
-                                                               } else if (error_code & 0x1000) != 0 {
-                                                                       let (real_code, error_data) = self.get_htlc_inbound_temp_fail_err_and_data(error_code, chan);
-                                                                       HTLCFailReason::reason(real_code, error_data)
-                                                               } else {
-                                                                       HTLCFailReason::from_failure_code(error_code)
-                                                               }.get_encrypted_failure_packet(incoming_shared_secret, &None);
-                                                               let msg = msgs::UpdateFailHTLC {
+                                                       pending_forward_info = PendingHTLCStatus::Fail(HTLCFailureMsg::Malformed(
+                                                               msgs::UpdateFailMalformedHTLC {
                                                                        channel_id: msg.channel_id,
                                                                        htlc_id: msg.htlc_id,
-                                                                       reason
-                                                               };
-                                                               PendingHTLCStatus::Fail(HTLCFailureMsg::Relay(msg))
-                                                       },
-                                                       _ => pending_forward_info
+                                                                       sha256_of_onion: [0; 32],
+                                                                       failure_code: INVALID_ONION_BLINDING,
+                                                               }
+                                                       ))
+                                               } else {
+                                                       match pending_forward_info {
+                                                               PendingHTLCStatus::Forward(PendingHTLCInfo {
+                                                                       ref incoming_shared_secret, ref routing, ..
+                                                               }) => {
+                                                                       let reason = if routing.blinded_failure().is_some() {
+                                                                               HTLCFailReason::reason(INVALID_ONION_BLINDING, vec![0; 32])
+                                                                       } else if (error_code & 0x1000) != 0 {
+                                                                               let (real_code, error_data) = self.get_htlc_inbound_temp_fail_err_and_data(error_code, chan);
+                                                                               HTLCFailReason::reason(real_code, error_data)
+                                                                       } else {
+                                                                               HTLCFailReason::from_failure_code(error_code)
+                                                                       }.get_encrypted_failure_packet(incoming_shared_secret, &None);
+                                                                       let msg = msgs::UpdateFailHTLC {
+                                                                               channel_id: msg.channel_id,
+                                                                               htlc_id: msg.htlc_id,
+                                                                               reason
+                                                                       };
+                                                                       pending_forward_info = PendingHTLCStatus::Fail(HTLCFailureMsg::Relay(msg));
+                                                               },
+                                                               _ => {},
+                                                       }
                                                }
-                                       };
-                                       let logger = WithChannelContext::from(&self.logger, &chan.context);
-                                       try_chan_phase_entry!(self, chan.update_add_htlc(&msg, pending_forward_info, create_pending_htlc_status, &self.fee_estimator, &&logger), chan_phase_entry);
+                                       }
+                                       try_chan_phase_entry!(self, chan.update_add_htlc(&msg, pending_forward_info, &self.fee_estimator), chan_phase_entry);
                                } else {
                                        return try_chan_phase_entry!(self, Err(ChannelError::Close(
                                                "Got an update_add_htlc message for an unfunded channel!".into())), chan_phase_entry);
@@ -6824,6 +7713,7 @@ where
 
        fn internal_update_fulfill_htlc(&self, counterparty_node_id: &PublicKey, msg: &msgs::UpdateFulfillHTLC) -> Result<(), MsgHandleErrInternal> {
                let funding_txo;
+               let next_user_channel_id;
                let (htlc_source, forwarded_htlc_value, skimmed_fee_msat) = {
                        let per_peer_state = self.per_peer_state.read().unwrap();
                        let peer_state_mutex = per_peer_state.get(counterparty_node_id)
@@ -6838,7 +7728,7 @@ where
                                        if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
                                                let res = try_chan_phase_entry!(self, chan.update_fulfill_htlc(&msg), chan_phase_entry);
                                                if let HTLCSource::PreviousHopData(prev_hop) = &res.0 {
-                                                       let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                                       let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                                        log_trace!(logger,
                                                                "Holding the next revoke_and_ack from {} until the preimage is durably persisted in the inbound edge's ChannelMonitor",
                                                                msg.channel_id);
@@ -6853,6 +7743,7 @@ where
                                                // outbound HTLC is claimed. This is guaranteed to all complete before we
                                                // process the RAA as messages are processed from single peers serially.
                                                funding_txo = chan.context.get_funding_txo().expect("We won't accept a fulfill until funded");
+                                               next_user_channel_id = chan.context.get_user_id();
                                                res
                                        } else {
                                                return try_chan_phase_entry!(self, Err(ChannelError::Close(
@@ -6864,7 +7755,7 @@ where
                };
                self.claim_funds_internal(htlc_source, msg.payment_preimage.clone(),
                        Some(forwarded_htlc_value), skimmed_fee_msat, false, false, Some(*counterparty_node_id),
-                       funding_txo, msg.channel_id
+                       funding_txo, msg.channel_id, Some(next_user_channel_id),
                );
 
                Ok(())
@@ -6936,7 +7827,7 @@ where
                match peer_state.channel_by_id.entry(msg.channel_id) {
                        hash_map::Entry::Occupied(mut chan_phase_entry) => {
                                if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
-                                       let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                       let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                        let funding_txo = chan.context.get_funding_txo();
                                        let monitor_update_opt = try_chan_phase_entry!(self, chan.commitment_signed(&msg, &&logger), chan_phase_entry);
                                        if let Some(monitor_update) = monitor_update_opt {
@@ -6953,10 +7844,28 @@ where
                }
        }
 
+       fn push_decode_update_add_htlcs(&self, mut update_add_htlcs: (u64, Vec<msgs::UpdateAddHTLC>)) {
+               let mut push_forward_event = self.forward_htlcs.lock().unwrap().is_empty();
+               let mut decode_update_add_htlcs = self.decode_update_add_htlcs.lock().unwrap();
+               push_forward_event &= decode_update_add_htlcs.is_empty();
+               let scid = update_add_htlcs.0;
+               match decode_update_add_htlcs.entry(scid) {
+                       hash_map::Entry::Occupied(mut e) => { e.get_mut().append(&mut update_add_htlcs.1); },
+                       hash_map::Entry::Vacant(e) => { e.insert(update_add_htlcs.1); },
+               }
+               if push_forward_event { self.push_pending_forwards_ev(); }
+       }
+
        #[inline]
        fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, ChannelId, u128, Vec<(PendingHTLCInfo, u64)>)]) {
+               let push_forward_event = self.forward_htlcs_without_forward_event(per_source_pending_forwards);
+               if push_forward_event { self.push_pending_forwards_ev() }
+       }
+
+       #[inline]
+       fn forward_htlcs_without_forward_event(&self, per_source_pending_forwards: &mut [(u64, OutPoint, ChannelId, u128, Vec<(PendingHTLCInfo, u64)>)]) -> bool {
+               let mut push_forward_event = false;
                for &mut (prev_short_channel_id, prev_funding_outpoint, prev_channel_id, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards {
-                       let mut push_forward_event = false;
                        let mut new_intercept_events = VecDeque::new();
                        let mut failed_intercept_forwards = Vec::new();
                        if !pending_forwards.is_empty() {
@@ -6969,6 +7878,7 @@ where
                                        // Pull this now to avoid introducing a lock order with `forward_htlcs`.
                                        let is_our_scid = self.short_to_chan_info.read().unwrap().contains_key(&scid);
 
+                                       let decode_update_add_htlcs_empty = self.decode_update_add_htlcs.lock().unwrap().is_empty();
                                        let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
                                        let forward_htlcs_empty = forward_htlcs.is_empty();
                                        match forward_htlcs.entry(scid) {
@@ -6995,7 +7905,7 @@ where
                                                                                        prev_short_channel_id, prev_funding_outpoint, prev_channel_id, prev_htlc_id, prev_user_channel_id, forward_info });
                                                                        },
                                                                        hash_map::Entry::Occupied(_) => {
-                                                                               let logger = WithContext::from(&self.logger, None, Some(prev_channel_id));
+                                                                               let logger = WithContext::from(&self.logger, None, Some(prev_channel_id), Some(forward_info.payment_hash));
                                                                                log_info!(logger, "Failed to forward incoming HTLC: detected duplicate intercepted payment over short channel id {}", scid);
                                                                                let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
                                                                                        short_channel_id: prev_short_channel_id,
@@ -7017,9 +7927,7 @@ where
                                                        } else {
                                                                // We don't want to generate a PendingHTLCsForwardable event if only intercepted
                                                                // payments are being processed.
-                                                               if forward_htlcs_empty {
-                                                                       push_forward_event = true;
-                                                               }
+                                                               push_forward_event |= forward_htlcs_empty && decode_update_add_htlcs_empty;
                                                                entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
                                                                        prev_short_channel_id, prev_funding_outpoint, prev_channel_id, prev_htlc_id, prev_user_channel_id, forward_info })));
                                                        }
@@ -7029,15 +7937,15 @@ where
                        }
 
                        for (htlc_source, payment_hash, failure_reason, destination) in failed_intercept_forwards.drain(..) {
-                               self.fail_htlc_backwards_internal(&htlc_source, &payment_hash, &failure_reason, destination);
+                               push_forward_event |= self.fail_htlc_backwards_internal_without_forward_event(&htlc_source, &payment_hash, &failure_reason, destination);
                        }
 
                        if !new_intercept_events.is_empty() {
                                let mut events = self.pending_events.lock().unwrap();
                                events.append(&mut new_intercept_events);
                        }
-                       if push_forward_event { self.push_pending_forwards_ev() }
                }
+               push_forward_event
        }
 
        fn push_pending_forwards_ev(&self) {
@@ -7107,7 +8015,7 @@ where
                        match peer_state.channel_by_id.entry(msg.channel_id) {
                                hash_map::Entry::Occupied(mut chan_phase_entry) => {
                                        if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
-                                               let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                               let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                                let funding_txo_opt = chan.context.get_funding_txo();
                                                let mon_update_blocked = if let Some(funding_txo) = funding_txo_opt {
                                                        self.raa_monitor_updates_held(
@@ -7147,7 +8055,7 @@ where
                match peer_state.channel_by_id.entry(msg.channel_id) {
                        hash_map::Entry::Occupied(mut chan_phase_entry) => {
                                if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
-                                       let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                       let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                        try_chan_phase_entry!(self, chan.update_fee(&self.fee_estimator, &msg, &&logger), chan_phase_entry);
                                } else {
                                        return try_chan_phase_entry!(self, Err(ChannelError::Close(
@@ -7227,7 +8135,7 @@ where
                                        if were_node_one == msg_from_node_one {
                                                return Ok(NotifyOption::SkipPersistNoEvents);
                                        } else {
-                                               let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                               let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                                log_debug!(logger, "Received channel_update {:?} for channel {}.", msg, chan_id);
                                                let did_change = try_chan_phase_entry!(self, chan.channel_update(&msg), chan_phase_entry);
                                                // If nothing changed after applying their update, we don't need to bother
@@ -7247,7 +8155,6 @@ where
        }
 
        fn internal_channel_reestablish(&self, counterparty_node_id: &PublicKey, msg: &msgs::ChannelReestablish) -> Result<NotifyOption, MsgHandleErrInternal> {
-               let htlc_forwards;
                let need_lnd_workaround = {
                        let per_peer_state = self.per_peer_state.read().unwrap();
 
@@ -7259,7 +8166,7 @@ where
                                                msg.channel_id
                                        )
                                })?;
-                       let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(msg.channel_id));
+                       let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(msg.channel_id), None);
                        let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                        let peer_state = &mut *peer_state_lock;
                        match peer_state.channel_by_id.entry(msg.channel_id) {
@@ -7290,9 +8197,11 @@ where
                                                        }
                                                }
                                                let need_lnd_workaround = chan.context.workaround_lnd_bug_4006.take();
-                                               htlc_forwards = self.handle_channel_resumption(
+                                               let (htlc_forwards, decode_update_add_htlcs) = self.handle_channel_resumption(
                                                        &mut peer_state.pending_msg_events, chan, responses.raa, responses.commitment_update, responses.order,
-                                                       Vec::new(), None, responses.channel_ready, responses.announcement_sigs);
+                                                       Vec::new(), Vec::new(), None, responses.channel_ready, responses.announcement_sigs);
+                                               debug_assert!(htlc_forwards.is_none());
+                                               debug_assert!(decode_update_add_htlcs.is_none());
                                                if let Some(upd) = channel_update {
                                                        peer_state.pending_msg_events.push(upd);
                                                }
@@ -7338,16 +8247,10 @@ where
                        }
                };
 
-               let mut persist = NotifyOption::SkipPersistHandleEvents;
-               if let Some(forwards) = htlc_forwards {
-                       self.forward_htlcs(&mut [forwards][..]);
-                       persist = NotifyOption::DoPersist;
-               }
-
                if let Some(channel_ready_msg) = need_lnd_workaround {
                        self.internal_channel_ready(counterparty_node_id, &channel_ready_msg)?;
                }
-               Ok(persist)
+               Ok(NotifyOption::SkipPersistHandleEvents)
        }
 
        /// Process pending events from the [`chain::Watch`], returning whether any events were processed.
@@ -7361,12 +8264,12 @@ where
                        for monitor_event in monitor_events.drain(..) {
                                match monitor_event {
                                        MonitorEvent::HTLCEvent(htlc_update) => {
-                                               let logger = WithContext::from(&self.logger, counterparty_node_id, Some(channel_id));
+                                               let logger = WithContext::from(&self.logger, counterparty_node_id, Some(channel_id), Some(htlc_update.payment_hash));
                                                if let Some(preimage) = htlc_update.payment_preimage {
                                                        log_trace!(logger, "Claiming HTLC with preimage {} from our monitor", preimage);
                                                        self.claim_funds_internal(htlc_update.source, preimage,
                                                                htlc_update.htlc_value_satoshis.map(|v| v * 1000), None, true,
-                                                               false, counterparty_node_id, funding_outpoint, channel_id);
+                                                               false, counterparty_node_id, funding_outpoint, channel_id, None);
                                                } else {
                                                        log_trace!(logger, "Failing HTLC with hash {} from our monitor", &htlc_update.payment_hash);
                                                        let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id };
@@ -7374,7 +8277,7 @@ where
                                                        self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
                                                }
                                        },
-                                       MonitorEvent::HolderForceClosed(_funding_outpoint) => {
+                                       MonitorEvent::HolderForceClosed(_) | MonitorEvent::HolderForceClosedWithInfo { .. } => {
                                                let counterparty_node_id_opt = match counterparty_node_id {
                                                        Some(cp_id) => Some(cp_id),
                                                        None => {
@@ -7392,16 +8295,22 @@ where
                                                                let pending_msg_events = &mut peer_state.pending_msg_events;
                                                                if let hash_map::Entry::Occupied(chan_phase_entry) = peer_state.channel_by_id.entry(channel_id) {
                                                                        if let ChannelPhase::Funded(mut chan) = remove_channel_phase!(self, chan_phase_entry) {
-                                                                               failed_channels.push(chan.context.force_shutdown(false, ClosureReason::HolderForceClosed));
+                                                                               let reason = if let MonitorEvent::HolderForceClosedWithInfo { reason, .. } = monitor_event {
+                                                                                       reason
+                                                                               } else {
+                                                                                       ClosureReason::HolderForceClosed
+                                                                               };
+                                                                               failed_channels.push(chan.context.force_shutdown(false, reason.clone()));
                                                                                if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
-                                                                                       pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
+                                                                                       let mut pending_broadcast_messages = self.pending_broadcast_messages.lock().unwrap();
+                                                                                       pending_broadcast_messages.push(events::MessageSendEvent::BroadcastChannelUpdate {
                                                                                                msg: update
                                                                                        });
                                                                                }
                                                                                pending_msg_events.push(events::MessageSendEvent::HandleError {
                                                                                        node_id: chan.context.get_counterparty_node_id(),
                                                                                        action: msgs::ErrorAction::DisconnectPeer {
-                                                                                               msg: Some(msgs::ErrorMessage { channel_id: chan.context.channel_id(), data: "Channel force-closed".to_owned() })
+                                                                                               msg: Some(msgs::ErrorMessage { channel_id: chan.context.channel_id(), data: reason.to_string() })
                                                                                        },
                                                                                });
                                                                        }
@@ -7455,7 +8364,7 @@ where
                                                let counterparty_node_id = chan.context.get_counterparty_node_id();
                                                let funding_txo = chan.context.get_funding_txo();
                                                let (monitor_opt, holding_cell_failed_htlcs) =
-                                                       chan.maybe_free_holding_cell_htlcs(&self.fee_estimator, &&WithChannelContext::from(&self.logger, &chan.context));
+                                                       chan.maybe_free_holding_cell_htlcs(&self.fee_estimator, &&WithChannelContext::from(&self.logger, &chan.context, None));
                                                if !holding_cell_failed_htlcs.is_empty() {
                                                        failed_htlcs.push((holding_cell_failed_htlcs, *channel_id, counterparty_node_id));
                                                }
@@ -7562,7 +8471,7 @@ where
                                peer_state.channel_by_id.retain(|channel_id, phase| {
                                        match phase {
                                                ChannelPhase::Funded(chan) => {
-                                                       let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                                       let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                                        match chan.maybe_propose_closing_signed(&self.fee_estimator, &&logger) {
                                                                Ok((msg_opt, tx_opt, shutdown_result_opt)) => {
                                                                        if let Some(msg) = msg_opt {
@@ -7579,7 +8488,8 @@ where
                                                                                // We're done with this channel. We got a closing_signed and sent back
                                                                                // a closing_signed with a closing transaction to broadcast.
                                                                                if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
-                                                                                       pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
+                                                                                       let mut pending_broadcast_messages = self.pending_broadcast_messages.lock().unwrap();
+                                                                                       pending_broadcast_messages.push(events::MessageSendEvent::BroadcastChannelUpdate {
                                                                                                msg: update
                                                                                        });
                                                                                }
@@ -7666,13 +8576,9 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
        ///
        /// Errors if the parameterized [`Router`] is unable to create a blinded path for the offer.
        ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
        /// [`Offer`]: crate::offers::offer::Offer
        /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
-       pub fn create_offer_builder(
-               &$self, description: String
-       ) -> Result<$builder, Bolt12SemanticError> {
+       pub fn create_offer_builder(&$self) -> Result<$builder, Bolt12SemanticError> {
                let node_id = $self.get_our_node_id();
                let expanded_key = &$self.inbound_payment_key;
                let entropy = &*$self.entropy_source;
@@ -7680,7 +8586,7 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
 
                let path = $self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
                let builder = OfferBuilder::deriving_signing_pubkey(
-                       description, node_id, expanded_key, entropy, secp_ctx
+                       node_id, expanded_key, entropy, secp_ctx
                )
                        .chain_hash($self.chain_hash)
                        .path(path);
@@ -7732,15 +8638,13 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
        /// - `amount_msats` is invalid, or
        /// - the parameterized [`Router`] is unable to create a blinded path for the refund.
        ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
        /// [`Refund`]: crate::offers::refund::Refund
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        /// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
        /// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments
        pub fn create_refund_builder(
-               &$self, description: String, amount_msats: u64, absolute_expiry: Duration,
-               payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
+               &$self, amount_msats: u64, absolute_expiry: Duration, payment_id: PaymentId,
+               retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
        ) -> Result<$builder, Bolt12SemanticError> {
                let node_id = $self.get_our_node_id();
                let expanded_key = &$self.inbound_payment_key;
@@ -7749,7 +8653,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
 
                let path = $self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
                let builder = RefundBuilder::deriving_payer_id(
-                       description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
+                       node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
                )?
                        .chain_hash($self.chain_hash)
                        .absolute_expiry(absolute_expiry)
@@ -7882,14 +8786,7 @@ where
                        .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
 
                let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
-               if offer.paths().is_empty() {
-                       let message = new_pending_onion_message(
-                               OffersMessage::InvoiceRequest(invoice_request),
-                               Destination::Node(offer.signing_pubkey()),
-                               Some(reply_path),
-                       );
-                       pending_offers_messages.push(message);
-               } else {
+               if !offer.paths().is_empty() {
                        // Send as many invoice requests as there are paths in the offer (with an upper bound).
                        // Using only one path could result in a failure if the path no longer exists. But only
                        // one invoice for a given payment id will be paid, even if more than one is received.
@@ -7902,6 +8799,16 @@ where
                                );
                                pending_offers_messages.push(message);
                        }
+               } else if let Some(signing_pubkey) = offer.signing_pubkey() {
+                       let message = new_pending_onion_message(
+                               OffersMessage::InvoiceRequest(invoice_request),
+                               Destination::Node(signing_pubkey),
+                               Some(reply_path),
+                       );
+                       pending_offers_messages.push(message);
+               } else {
+                       debug_assert!(false);
+                       return Err(Bolt12SemanticError::MissingSigningPubkey);
                }
 
                Ok(())
@@ -7912,7 +8819,7 @@ where
        ///
        /// The resulting invoice uses a [`PaymentHash`] recognized by the [`ChannelManager`] and a
        /// [`BlindedPath`] containing the [`PaymentSecret`] needed to reconstruct the corresponding
-       /// [`PaymentPreimage`].
+       /// [`PaymentPreimage`]. It is returned purely for informational purposes.
        ///
        /// # Limitations
        ///
@@ -7929,7 +8836,9 @@ where
        ///   the invoice.
        ///
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
-       pub fn request_refund_payment(&self, refund: &Refund) -> Result<(), Bolt12SemanticError> {
+       pub fn request_refund_payment(
+               &self, refund: &Refund
+       ) -> Result<Bolt12Invoice, Bolt12SemanticError> {
                let expanded_key = &self.inbound_payment_key;
                let entropy = &*self.entropy_source;
                let secp_ctx = &self.secp_ctx;
@@ -7945,7 +8854,10 @@ where
 
                match self.create_inbound_payment(Some(amount_msats), relative_expiry, None) {
                        Ok((payment_hash, payment_secret)) => {
-                               let payment_paths = self.create_blinded_payment_paths(amount_msats, payment_secret)
+                               let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {});
+                               let payment_paths = self.create_blinded_payment_paths(
+                                       amount_msats, payment_secret, payment_context
+                               )
                                        .map_err(|_| Bolt12SemanticError::MissingPaths)?;
 
                                #[cfg(feature = "std")]
@@ -7968,7 +8880,7 @@ where
                                let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
                                if refund.paths().is_empty() {
                                        let message = new_pending_onion_message(
-                                               OffersMessage::Invoice(invoice),
+                                               OffersMessage::Invoice(invoice.clone()),
                                                Destination::Node(refund.payer_id()),
                                                Some(reply_path),
                                        );
@@ -7984,7 +8896,7 @@ where
                                        }
                                }
 
-                               Ok(())
+                               Ok(invoice)
                        },
                        Err(()) => Err(Bolt12SemanticError::InvalidAmount),
                }
@@ -7996,10 +8908,9 @@ where
        /// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
        /// [`PaymentHash`] and [`PaymentPreimage`] for you.
        ///
-       /// The [`PaymentPreimage`] will ultimately be returned to you in the [`PaymentClaimable`], which
-       /// will have the [`PaymentClaimable::purpose`] be [`PaymentPurpose::InvoicePayment`] with
-       /// its [`PaymentPurpose::InvoicePayment::payment_preimage`] field filled in. That should then be
-       /// passed directly to [`claim_funds`].
+       /// The [`PaymentPreimage`] will ultimately be returned to you in the [`PaymentClaimable`] event, which
+       /// will have the [`PaymentClaimable::purpose`] return `Some` for [`PaymentPurpose::preimage`]. That
+       /// should then be passed directly to [`claim_funds`].
        ///
        /// See [`create_inbound_payment_for_hash`] for detailed documentation on behavior and requirements.
        ///
@@ -8019,8 +8930,7 @@ where
        /// [`claim_funds`]: Self::claim_funds
        /// [`PaymentClaimable`]: events::Event::PaymentClaimable
        /// [`PaymentClaimable::purpose`]: events::Event::PaymentClaimable::purpose
-       /// [`PaymentPurpose::InvoicePayment`]: events::PaymentPurpose::InvoicePayment
-       /// [`PaymentPurpose::InvoicePayment::payment_preimage`]: events::PaymentPurpose::InvoicePayment::payment_preimage
+       /// [`PaymentPurpose::preimage`]: events::PaymentPurpose::preimage
        /// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
        pub fn create_inbound_payment(&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
                min_final_cltv_expiry_delta: Option<u16>) -> Result<(PaymentHash, PaymentSecret), ()> {
@@ -8099,8 +9009,16 @@ where
 
                let peers = self.per_peer_state.read().unwrap()
                        .iter()
-                       .filter(|(_, peer)| peer.lock().unwrap().latest_features.supports_onion_messages())
-                       .map(|(node_id, _)| *node_id)
+                       .map(|(node_id, peer_state)| (node_id, peer_state.lock().unwrap()))
+                       .filter(|(_, peer)| peer.latest_features.supports_onion_messages())
+                       .map(|(node_id, peer)| ForwardNode {
+                               node_id: *node_id,
+                               short_channel_id: peer.channel_by_id
+                                       .iter()
+                                       .filter(|(_, channel)| channel.context().is_usable())
+                                       .min_by_key(|(_, channel)| channel.context().channel_creation_height)
+                                       .and_then(|(_, channel)| channel.context().get_short_channel_id()),
+                       })
                        .collect::<Vec<_>>();
 
                self.router
@@ -8111,7 +9029,7 @@ where
        /// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to
        /// [`Router::create_blinded_payment_paths`].
        fn create_blinded_payment_paths(
-               &self, amount_msats: u64, payment_secret: PaymentSecret
+               &self, amount_msats: u64, payment_secret: PaymentSecret, payment_context: PaymentContext
        ) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
                let secp_ctx = &self.secp_ctx;
 
@@ -8125,6 +9043,7 @@ where
                                max_cltv_expiry,
                                htlc_minimum_msat: 1,
                        },
+                       payment_context,
                };
                self.router.create_blinded_payment_paths(
                        payee_node_id, first_hops, payee_tlvs, amount_msats, secp_ctx
@@ -8238,7 +9157,7 @@ where
                mut completed_blocker: Option<RAAMonitorUpdateBlockingAction>) {
 
                let logger = WithContext::from(
-                       &self.logger, Some(counterparty_node_id), Some(channel_id),
+                       &self.logger, Some(counterparty_node_id), Some(channel_id), None
                );
                loop {
                        let per_peer_state = self.per_peer_state.read().unwrap();
@@ -8339,7 +9258,7 @@ where
        /// will randomly be placed first or last in the returned array.
        ///
        /// Note that even though `BroadcastChannelAnnouncement` and `BroadcastChannelUpdate`
-       /// `MessageSendEvent`s are intended to be broadcasted to all peers, they will be pleaced among
+       /// `MessageSendEvent`s are intended to be broadcasted to all peers, they will be placed among
        /// the `MessageSendEvent`s to the specific peer they were generated under.
        fn get_and_clear_pending_msg_events(&self) -> Vec<MessageSendEvent> {
                let events = RefCell::new(Vec::new());
@@ -8359,6 +9278,7 @@ where
                                result = NotifyOption::DoPersist;
                        }
 
+                       let mut is_any_peer_connected = false;
                        let mut pending_events = Vec::new();
                        let per_peer_state = self.per_peer_state.read().unwrap();
                        for (_cp_id, peer_state_mutex) in per_peer_state.iter() {
@@ -8367,6 +9287,15 @@ where
                                if peer_state.pending_msg_events.len() > 0 {
                                        pending_events.append(&mut peer_state.pending_msg_events);
                                }
+                               if peer_state.is_connected {
+                                       is_any_peer_connected = true
+                               }
+                       }
+
+                       // Ensure that we are connected to some peers before getting broadcast messages.
+                       if is_any_peer_connected {
+                               let mut broadcast_msgs = self.pending_broadcast_messages.lock().unwrap();
+                               pending_events.append(&mut broadcast_msgs);
                        }
 
                        if !pending_events.is_empty() {
@@ -8438,7 +9367,7 @@ where
                        *best_block = BestBlock::new(header.prev_blockhash, new_height)
                }
 
-               self.do_chain_event(Some(new_height), |channel| channel.best_block_updated(new_height, header.time, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context)));
+               self.do_chain_event(Some(new_height), |channel| channel.best_block_updated(new_height, header.time, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context, None)));
        }
 }
 
@@ -8464,13 +9393,13 @@ where
                let _persistence_guard =
                        PersistenceNotifierGuard::optionally_notify_skipping_background_events(
                                self, || -> NotifyOption { NotifyOption::DoPersist });
-               self.do_chain_event(Some(height), |channel| channel.transactions_confirmed(&block_hash, height, txdata, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context))
+               self.do_chain_event(Some(height), |channel| channel.transactions_confirmed(&block_hash, height, txdata, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context, None))
                        .map(|(a, b)| (a, Vec::new(), b)));
 
                let last_best_block_height = self.best_block.read().unwrap().height;
                if height < last_best_block_height {
                        let timestamp = self.highest_seen_timestamp.load(Ordering::Acquire);
-                       self.do_chain_event(Some(last_best_block_height), |channel| channel.best_block_updated(last_best_block_height, timestamp as u32, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context)));
+                       self.do_chain_event(Some(last_best_block_height), |channel| channel.best_block_updated(last_best_block_height, timestamp as u32, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context, None)));
                }
        }
 
@@ -8487,7 +9416,7 @@ where
                                self, || -> NotifyOption { NotifyOption::DoPersist });
                *self.best_block.write().unwrap() = BestBlock::new(block_hash, height);
 
-               self.do_chain_event(Some(height), |channel| channel.best_block_updated(height, header.time, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context)));
+               self.do_chain_event(Some(height), |channel| channel.best_block_updated(height, header.time, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context, None)));
 
                macro_rules! max_time {
                        ($timestamp: expr) => {
@@ -8536,7 +9465,7 @@ where
                self.do_chain_event(None, |channel| {
                        if let Some(funding_txo) = channel.context.get_funding_txo() {
                                if funding_txo.txid == *txid {
-                                       channel.funding_transaction_unconfirmed(&&WithChannelContext::from(&self.logger, &channel.context)).map(|()| (None, Vec::new(), None))
+                                       channel.funding_transaction_unconfirmed(&&WithChannelContext::from(&self.logger, &channel.context, None)).map(|()| (None, Vec::new(), None))
                                } else { Ok((None, Vec::new(), None)) }
                        } else { Ok((None, Vec::new(), None)) }
                });
@@ -8571,12 +9500,13 @@ where
                                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                                let peer_state = &mut *peer_state_lock;
                                let pending_msg_events = &mut peer_state.pending_msg_events;
+
                                peer_state.channel_by_id.retain(|_, phase| {
                                        match phase {
                                                // Retain unfunded channels.
                                                ChannelPhase::UnfundedOutboundV1(_) | ChannelPhase::UnfundedInboundV1(_) => true,
                                                // TODO(dual_funding): Combine this match arm with above.
-                                               #[cfg(dual_funding)]
+                                               #[cfg(any(dual_funding, splicing))]
                                                ChannelPhase::UnfundedOutboundV2(_) | ChannelPhase::UnfundedInboundV2(_) => true,
                                                ChannelPhase::Funded(channel) => {
                                                        let res = f(channel);
@@ -8586,7 +9516,7 @@ where
                                                                        timed_out_htlcs.push((source, payment_hash, HTLCFailReason::reason(failure_code, data),
                                                                                HTLCDestination::NextHopChannel { node_id: Some(channel.context.get_counterparty_node_id()), channel_id: channel.context.channel_id() }));
                                                                }
-                                                               let logger = WithChannelContext::from(&self.logger, &channel.context);
+                                                               let logger = WithChannelContext::from(&self.logger, &channel.context, None);
                                                                if let Some(channel_ready) = channel_ready_opt {
                                                                        send_channel_ready!(self, pending_msg_events, channel, channel_ready);
                                                                        if channel.context.is_usable() {
@@ -8646,7 +9576,8 @@ where
                                                                let reason_message = format!("{}", reason);
                                                                failed_channels.push(channel.context.force_shutdown(true, reason));
                                                                if let Ok(update) = self.get_channel_update_for_broadcast(&channel) {
-                                                                       pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
+                                                                       let mut pending_broadcast_messages = self.pending_broadcast_messages.lock().unwrap();
+                                                                       pending_broadcast_messages.push(events::MessageSendEvent::BroadcastChannelUpdate {
                                                                                msg: update
                                                                        });
                                                                }
@@ -8710,7 +9641,7 @@ where
                                                        HTLCFailReason::from_failure_code(0x2000 | 2),
                                                        HTLCDestination::InvalidForward { requested_forward_scid }));
                                        let logger = WithContext::from(
-                                               &self.logger, None, Some(htlc.prev_channel_id)
+                                               &self.logger, None, Some(htlc.prev_channel_id), Some(htlc.forward_info.payment_hash)
                                        );
                                        log_trace!(logger, "Timing out intercepted HTLC with requested forward scid {}", requested_forward_scid);
                                        false
@@ -8738,6 +9669,9 @@ where
        }
 
        /// Returns true if this [`ChannelManager`] needs to be persisted.
+       ///
+       /// See [`Self::get_event_or_persistence_needed_future`] for retrieving a [`Future`] that
+       /// indicates this should be checked.
        pub fn get_and_clear_needs_persistence(&self) -> bool {
                self.needs_persist_flag.swap(false, Ordering::AcqRel)
        }
@@ -8878,18 +9812,21 @@ where
                         msg.channel_id.clone())), *counterparty_node_id);
        }
 
+       #[cfg(splicing)]
        fn handle_splice(&self, counterparty_node_id: &PublicKey, msg: &msgs::Splice) {
                let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
                        "Splicing not supported".to_owned(),
                         msg.channel_id.clone())), *counterparty_node_id);
        }
 
+       #[cfg(splicing)]
        fn handle_splice_ack(&self, counterparty_node_id: &PublicKey, msg: &msgs::SpliceAck) {
                let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
                        "Splicing not supported (splice_ack)".to_owned(),
                         msg.channel_id.clone())), *counterparty_node_id);
        }
 
+       #[cfg(splicing)]
        fn handle_splice_locked(&self, counterparty_node_id: &PublicKey, msg: &msgs::SpliceLocked) {
                let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
                        "Splicing not supported (splice_locked)".to_owned(),
@@ -9020,7 +9957,7 @@ where
                let mut per_peer_state = self.per_peer_state.write().unwrap();
                let remove_peer = {
                        log_debug!(
-                               WithContext::from(&self.logger, Some(*counterparty_node_id), None),
+                               WithContext::from(&self.logger, Some(*counterparty_node_id), None, None),
                                "Marking channels with {} disconnected and generating channel_updates.",
                                log_pubkey!(counterparty_node_id)
                        );
@@ -9031,27 +9968,30 @@ where
                                peer_state.channel_by_id.retain(|_, phase| {
                                        let context = match phase {
                                                ChannelPhase::Funded(chan) => {
-                                                       let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                                       let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                                        if chan.remove_uncommitted_htlcs_and_mark_paused(&&logger).is_ok() {
                                                                // We only retain funded channels that are not shutdown.
                                                                return true;
                                                        }
                                                        &mut chan.context
                                                },
-                                               // We retain UnfundedOutboundV1 channel for some time in case
-                                               // peer unexpectedly disconnects, and intends to reconnect again.
-                                               ChannelPhase::UnfundedOutboundV1(_) => {
-                                                       return true;
-                                               },
+                                               // If we get disconnected and haven't yet committed to a funding
+                                               // transaction, we can replay the `open_channel` on reconnection, so don't
+                                               // bother dropping the channel here. However, if we already committed to
+                                               // the funding transaction we don't yet support replaying the funding
+                                               // handshake (and bailing if the peer rejects it), so we force-close in
+                                               // that case.
+                                               ChannelPhase::UnfundedOutboundV1(chan) if chan.is_resumable() => return true,
+                                               ChannelPhase::UnfundedOutboundV1(chan) => &mut chan.context,
                                                // Unfunded inbound channels will always be removed.
                                                ChannelPhase::UnfundedInboundV1(chan) => {
                                                        &mut chan.context
                                                },
-                                               #[cfg(dual_funding)]
+                                               #[cfg(any(dual_funding, splicing))]
                                                ChannelPhase::UnfundedOutboundV2(chan) => {
                                                        &mut chan.context
                                                },
-                                               #[cfg(dual_funding)]
+                                               #[cfg(any(dual_funding, splicing))]
                                                ChannelPhase::UnfundedInboundV2(chan) => {
                                                        &mut chan.context
                                                },
@@ -9103,7 +10043,12 @@ where
                                                // Gossip
                                                &events::MessageSendEvent::SendChannelAnnouncement { .. } => false,
                                                &events::MessageSendEvent::BroadcastChannelAnnouncement { .. } => true,
-                                               &events::MessageSendEvent::BroadcastChannelUpdate { .. } => true,
+                                               // [`ChannelManager::pending_broadcast_events`] holds the [`BroadcastChannelUpdate`]
+                                               // This check here is to ensure exhaustivity.
+                                               &events::MessageSendEvent::BroadcastChannelUpdate { .. } => {
+                                                       debug_assert!(false, "This event shouldn't have been here");
+                                                       false
+                                               },
                                                &events::MessageSendEvent::BroadcastNodeAnnouncement { .. } => true,
                                                &events::MessageSendEvent::SendChannelUpdate { .. } => false,
                                                &events::MessageSendEvent::SendChannelRangeQuery { .. } => false,
@@ -9128,7 +10073,7 @@ where
        }
 
        fn peer_connected(&self, counterparty_node_id: &PublicKey, init_msg: &msgs::Init, inbound: bool) -> Result<(), ()> {
-               let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), None);
+               let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), None, None);
                if !init_msg.features.supports_static_remote_key() {
                        log_debug!(logger, "Peer {} does not support static remote key, disconnecting", log_pubkey!(counterparty_node_id));
                        return Err(());
@@ -9193,7 +10138,7 @@ where
                                for (_, phase) in peer_state.channel_by_id.iter_mut() {
                                        match phase {
                                                ChannelPhase::Funded(chan) => {
-                                                       let logger = WithChannelContext::from(&self.logger, &chan.context);
+                                                       let logger = WithChannelContext::from(&self.logger, &chan.context, None);
                                                        pending_msg_events.push(events::MessageSendEvent::SendChannelReestablish {
                                                                node_id: chan.context.get_counterparty_node_id(),
                                                                msg: chan.get_channel_reestablish(&&logger),
@@ -9207,8 +10152,8 @@ where
                                                        });
                                                }
 
-                                               // TODO(dual_funding): Combine this match arm with above once #[cfg(dual_funding)] is removed.
-                                               #[cfg(dual_funding)]
+                                               // TODO(dual_funding): Combine this match arm with above once #[cfg(any(dual_funding, splicing))] is removed.
+                                               #[cfg(any(dual_funding, splicing))]
                                                ChannelPhase::UnfundedOutboundV2(chan) => {
                                                        pending_msg_events.push(events::MessageSendEvent::SendOpenChannelV2 {
                                                                node_id: chan.context.get_counterparty_node_id(),
@@ -9223,8 +10168,8 @@ where
                                                        debug_assert!(false);
                                                }
 
-                                               // TODO(dual_funding): Combine this match arm with above once #[cfg(dual_funding)] is removed.
-                                               #[cfg(dual_funding)]
+                                               // TODO(dual_funding): Combine this match arm with above once #[cfg(any(dual_funding, splicing))] is removed.
+                                               #[cfg(any(dual_funding, splicing))]
                                                ChannelPhase::UnfundedInboundV2(channel) => {
                                                        // Since unfunded inbound channel maps are cleared upon disconnecting a peer,
                                                        // they are not persisted and won't be recovered after a crash.
@@ -9327,7 +10272,7 @@ where
                                                        return;
                                                }
                                        },
-                                       #[cfg(dual_funding)]
+                                       #[cfg(any(dual_funding, splicing))]
                                        Some(ChannelPhase::UnfundedOutboundV2(ref mut chan)) => {
                                                if let Ok(msg) = chan.maybe_handle_error_without_close(self.chain_hash, &self.fee_estimator) {
                                                        peer_state.pending_msg_events.push(events::MessageSendEvent::SendOpenChannelV2 {
@@ -9338,7 +10283,7 @@ where
                                                }
                                        },
                                        None | Some(ChannelPhase::UnfundedInboundV1(_) | ChannelPhase::Funded(_)) => (),
-                                       #[cfg(dual_funding)]
+                                       #[cfg(any(dual_funding, splicing))]
                                        Some(ChannelPhase::UnfundedInboundV2(_)) => (),
                                }
                        }
@@ -9427,23 +10372,27 @@ where
        R::Target: Router,
        L::Target: Logger,
 {
-       fn handle_message(&self, message: OffersMessage) -> Option<OffersMessage> {
+       fn handle_message(&self, message: OffersMessage, responder: Option<Responder>) -> ResponseInstruction<OffersMessage> {
                let secp_ctx = &self.secp_ctx;
                let expanded_key = &self.inbound_payment_key;
 
                match message {
                        OffersMessage::InvoiceRequest(invoice_request) => {
+                               let responder = match responder {
+                                       Some(responder) => responder,
+                                       None => return ResponseInstruction::NoResponse,
+                               };
                                let amount_msats = match InvoiceBuilder::<DerivedSigningPubkey>::amount_msats(
                                        &invoice_request
                                ) {
                                        Ok(amount_msats) => amount_msats,
-                                       Err(error) => return Some(OffersMessage::InvoiceError(error.into())),
+                                       Err(error) => return responder.respond(OffersMessage::InvoiceError(error.into())),
                                };
                                let invoice_request = match invoice_request.verify(expanded_key, secp_ctx) {
                                        Ok(invoice_request) => invoice_request,
                                        Err(()) => {
                                                let error = Bolt12SemanticError::InvalidMetadata;
-                                               return Some(OffersMessage::InvoiceError(error.into()));
+                                               return responder.respond(OffersMessage::InvoiceError(error.into()));
                                        },
                                };
 
@@ -9454,17 +10403,21 @@ where
                                        Ok((payment_hash, payment_secret)) => (payment_hash, payment_secret),
                                        Err(()) => {
                                                let error = Bolt12SemanticError::InvalidAmount;
-                                               return Some(OffersMessage::InvoiceError(error.into()));
+                                               return responder.respond(OffersMessage::InvoiceError(error.into()));
                                        },
                                };
 
+                               let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext {
+                                       offer_id: invoice_request.offer_id,
+                                       invoice_request: invoice_request.fields(),
+                               });
                                let payment_paths = match self.create_blinded_payment_paths(
-                                       amount_msats, payment_secret
+                                       amount_msats, payment_secret, payment_context
                                ) {
                                        Ok(payment_paths) => payment_paths,
                                        Err(()) => {
                                                let error = Bolt12SemanticError::MissingPaths;
-                                               return Some(OffersMessage::InvoiceError(error.into()));
+                                               return responder.respond(OffersMessage::InvoiceError(error.into()));
                                        },
                                };
 
@@ -9473,7 +10426,7 @@ where
                                        self.highest_seen_timestamp.load(Ordering::Acquire) as u64
                                );
 
-                               if invoice_request.keys.is_some() {
+                               let response = if invoice_request.keys.is_some() {
                                        #[cfg(feature = "std")]
                                        let builder = invoice_request.respond_using_derived_keys(
                                                payment_paths, payment_hash
@@ -9482,12 +10435,10 @@ where
                                        let builder = invoice_request.respond_using_derived_keys_no_std(
                                                payment_paths, payment_hash, created_at
                                        );
-                                       let builder: Result<InvoiceBuilder<DerivedSigningPubkey>, _> =
-                                               builder.map(|b| b.into());
-                                       match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) {
-                                               Ok(invoice) => Some(OffersMessage::Invoice(invoice)),
-                                               Err(error) => Some(OffersMessage::InvoiceError(error.into())),
-                                       }
+                                       builder
+                                               .map(InvoiceBuilder::<DerivedSigningPubkey>::from)
+                                               .and_then(|builder| builder.allow_mpp().build_and_sign(secp_ctx))
+                                               .map_err(InvoiceError::from)
                                } else {
                                        #[cfg(feature = "std")]
                                        let builder = invoice_request.respond_with(payment_paths, payment_hash);
@@ -9495,52 +10446,58 @@ where
                                        let builder = invoice_request.respond_with_no_std(
                                                payment_paths, payment_hash, created_at
                                        );
-                                       let builder: Result<InvoiceBuilder<ExplicitSigningPubkey>, _> =
-                                               builder.map(|b| b.into());
-                                       let response = builder.and_then(|builder| builder.allow_mpp().build())
-                                               .map_err(|e| OffersMessage::InvoiceError(e.into()))
+                                       builder
+                                               .map(InvoiceBuilder::<ExplicitSigningPubkey>::from)
+                                               .and_then(|builder| builder.allow_mpp().build())
+                                               .map_err(InvoiceError::from)
                                                .and_then(|invoice| {
                                                        #[cfg(c_bindings)]
                                                        let mut invoice = invoice;
-                                                       match invoice.sign(|invoice: &UnsignedBolt12Invoice|
-                                                               self.node_signer.sign_bolt12_invoice(invoice)
-                                                       ) {
-                                                               Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
-                                                               Err(SignError::Signing) => Err(OffersMessage::InvoiceError(
-                                                                               InvoiceError::from_string("Failed signing invoice".to_string())
-                                                               )),
-                                                               Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError(
-                                                                               InvoiceError::from_string("Failed invoice signature verification".to_string())
-                                                               )),
-                                                       }
-                                               });
-                                       match response {
-                                               Ok(invoice) => Some(invoice),
-                                               Err(error) => Some(error),
-                                       }
+                                                       invoice
+                                                               .sign(|invoice: &UnsignedBolt12Invoice|
+                                                                       self.node_signer.sign_bolt12_invoice(invoice)
+                                                               )
+                                                               .map_err(InvoiceError::from)
+                                               })
+                               };
+
+                               match response {
+                                       Ok(invoice) => return responder.respond(OffersMessage::Invoice(invoice)),
+                                       Err(error) => return responder.respond(OffersMessage::InvoiceError(error.into())),
                                }
                        },
                        OffersMessage::Invoice(invoice) => {
-                               match invoice.verify(expanded_key, secp_ctx) {
-                                       Err(()) => {
-                                               Some(OffersMessage::InvoiceError(InvoiceError::from_string("Unrecognized invoice".to_owned())))
-                                       },
-                                       Ok(_) if invoice.invoice_features().requires_unknown_bits_from(&self.bolt12_invoice_features()) => {
-                                               Some(OffersMessage::InvoiceError(Bolt12SemanticError::UnknownRequiredFeatures.into()))
-                                       },
-                                       Ok(payment_id) => {
-                                               if let Err(e) = self.send_payment_for_bolt12_invoice(&invoice, payment_id) {
-                                                       log_trace!(self.logger, "Failed paying invoice: {:?}", e);
-                                                       Some(OffersMessage::InvoiceError(InvoiceError::from_string(format!("{:?}", e))))
+                               let response = invoice
+                                       .verify(expanded_key, secp_ctx)
+                                       .map_err(|()| InvoiceError::from_string("Unrecognized invoice".to_owned()))
+                                       .and_then(|payment_id| {
+                                               let features = self.bolt12_invoice_features();
+                                               if invoice.invoice_features().requires_unknown_bits_from(&features) {
+                                                       Err(InvoiceError::from(Bolt12SemanticError::UnknownRequiredFeatures))
                                                } else {
-                                                       None
+                                                       self.send_payment_for_bolt12_invoice(&invoice, payment_id)
+                                                               .map_err(|e| {
+                                                                       log_trace!(self.logger, "Failed paying invoice: {:?}", e);
+                                                                       InvoiceError::from_string(format!("{:?}", e))
+                                                               })
                                                }
-                                       },
+                                       });
+
+                               match (responder, response) {
+                                       (Some(responder), Err(e)) => responder.respond(OffersMessage::InvoiceError(e)),
+                                       (None, Err(_)) => {
+                                               log_trace!(
+                                                       self.logger,
+                                                       "A response was generated, but there is no reply_path specified for sending the response."
+                                               );
+                                               return ResponseInstruction::NoResponse;
+                                       }
+                                       _ => return ResponseInstruction::NoResponse,
                                }
                        },
                        OffersMessage::InvoiceError(invoice_error) => {
                                log_trace!(self.logger, "Received invoice_error: {}", invoice_error);
-                               None
+                               return ResponseInstruction::NoResponse;
                        },
                }
        }
@@ -9550,6 +10507,23 @@ where
        }
 }
 
+impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>
+NodeIdLookUp for ChannelManager<M, T, ES, NS, SP, F, R, L>
+where
+       M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
+       T::Target: BroadcasterInterface,
+       ES::Target: EntropySource,
+       NS::Target: NodeSigner,
+       SP::Target: SignerProvider,
+       F::Target: FeeEstimator,
+       R::Target: Router,
+       L::Target: Logger,
+{
+       fn next_node_id(&self, short_channel_id: u64) -> Option<PublicKey> {
+               self.short_to_chan_info.read().unwrap().get(&short_channel_id).map(|(pubkey, _)| *pubkey)
+       }
+}
+
 /// Fetches the set of [`NodeFeatures`] flags that are provided by or required by
 /// [`ChannelManager`].
 pub(crate) fn provided_node_features(config: &UserConfig) -> NodeFeatures {
@@ -9772,9 +10746,11 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
                (3, payment_metadata, option),
                (5, custom_tlvs, optional_vec),
                (7, requires_blinded_error, (default_value, false)),
+               (9, payment_context, option),
        },
        (2, ReceiveKeysend) => {
                (0, payment_preimage, required),
+               (1, requires_blinded_error, (default_value, false)),
                (2, incoming_cltv_expiry, required),
                (3, payment_metadata, option),
                (4, payment_data, option), // Added in 0.0.116
@@ -9886,7 +10862,9 @@ impl_writeable_tlv_based!(HTLCPreviousHopData, {
 impl Writeable for ClaimableHTLC {
        fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
                let (payment_data, keysend_preimage) = match &self.onion_payload {
-                       OnionPayload::Invoice { _legacy_hop_data } => (_legacy_hop_data.as_ref(), None),
+                       OnionPayload::Invoice { _legacy_hop_data } => {
+                               (_legacy_hop_data.as_ref(), None)
+                       },
                        OnionPayload::Spontaneous(preimage) => (None, Some(preimage)),
                };
                write_tlv_fields!(writer, {
@@ -10131,9 +11109,10 @@ where
                        best_block.block_hash.write(writer)?;
                }
 
+               let per_peer_state = self.per_peer_state.write().unwrap();
+
                let mut serializable_peer_count: u64 = 0;
                {
-                       let per_peer_state = self.per_peer_state.read().unwrap();
                        let mut number_of_funded_channels = 0;
                        for (_, peer_state_mutex) in per_peer_state.iter() {
                                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
@@ -10174,7 +11153,11 @@ where
                        }
                }
 
-               let per_peer_state = self.per_peer_state.write().unwrap();
+               let mut decode_update_add_htlcs_opt = None;
+               let decode_update_add_htlcs = self.decode_update_add_htlcs.lock().unwrap();
+               if !decode_update_add_htlcs.is_empty() {
+                       decode_update_add_htlcs_opt = Some(decode_update_add_htlcs);
+               }
 
                let pending_inbound_payments = self.pending_inbound_payments.lock().unwrap();
                let claimable_payments = self.claimable_payments.lock().unwrap();
@@ -10325,6 +11308,7 @@ where
                        (10, in_flight_monitor_updates, option),
                        (11, self.probing_cookie_secret, required),
                        (13, htlc_onion_fields, optional_vec),
+                       (14, decode_update_add_htlcs_opt, option),
                });
 
                Ok(())
@@ -10555,7 +11539,7 @@ where
                        let mut channel: Channel<SP> = Channel::read(reader, (
                                &args.entropy_source, &args.signer_provider, best_block_height, &provided_channel_type_features(&args.default_config)
                        ))?;
-                       let logger = WithChannelContext::from(&args.logger, &channel.context);
+                       let logger = WithChannelContext::from(&args.logger, &channel.context, None);
                        let funding_txo = channel.context.get_funding_txo().ok_or(DecodeError::InvalidValue)?;
                        funding_txo_to_channel_id.insert(funding_txo, channel.context.channel_id());
                        funding_txo_set.insert(funding_txo.clone());
@@ -10614,6 +11598,7 @@ where
                                                        // claim update ChannelMonitor updates were persisted prior to persising
                                                        // the ChannelMonitor update for the forward leg, so attempting to fail the
                                                        // backwards leg of the HTLC will simply be rejected.
+                                                       let logger = WithChannelContext::from(&args.logger, &channel.context, Some(*payment_hash));
                                                        log_info!(logger,
                                                                "Failing HTLC with hash {} as it is missing in the ChannelMonitor for channel {} but was present in the (stale) ChannelManager",
                                                                &channel.context.channel_id(), &payment_hash);
@@ -10621,9 +11606,10 @@ where
                                                }
                                        }
                                } else {
-                                       log_info!(logger, "Successfully loaded channel {} at update_id {} against monitor at update id {}",
+                                       channel.on_startup_drop_completed_blocked_mon_updates_through(&logger, monitor.get_latest_update_id());
+                                       log_info!(logger, "Successfully loaded channel {} at update_id {} against monitor at update id {} with {} blocked updates",
                                                &channel.context.channel_id(), channel.context.get_latest_monitor_update_id(),
-                                               monitor.get_latest_update_id());
+                                               monitor.get_latest_update_id(), channel.blocked_monitor_updates_pending());
                                        if let Some(short_channel_id) = channel.context.get_short_channel_id() {
                                                short_to_chan_info.insert(short_channel_id, (channel.context.get_counterparty_node_id(), channel.context.channel_id()));
                                        }
@@ -10667,7 +11653,7 @@ where
 
                for (funding_txo, monitor) in args.channel_monitors.iter() {
                        if !funding_txo_set.contains(funding_txo) {
-                               let logger = WithChannelMonitor::from(&args.logger, monitor);
+                               let logger = WithChannelMonitor::from(&args.logger, monitor, None);
                                let channel_id = monitor.channel_id();
                                log_info!(logger, "Queueing monitor update to ensure missing channel {} is force closed",
                                        &channel_id);
@@ -10790,6 +11776,7 @@ where
                let mut monitor_update_blocked_actions_per_peer: Option<Vec<(_, BTreeMap<_, Vec<_>>)>> = Some(Vec::new());
                let mut events_override = None;
                let mut in_flight_monitor_updates: Option<HashMap<(PublicKey, OutPoint), Vec<ChannelMonitorUpdate>>> = None;
+               let mut decode_update_add_htlcs: Option<HashMap<u64, Vec<msgs::UpdateAddHTLC>>> = None;
                read_tlv_fields!(reader, {
                        (1, pending_outbound_payments_no_retry, option),
                        (2, pending_intercepted_htlcs, option),
@@ -10803,7 +11790,9 @@ where
                        (10, in_flight_monitor_updates, option),
                        (11, probing_cookie_secret, option),
                        (13, claimable_htlc_onion_fields, optional_vec),
+                       (14, decode_update_add_htlcs, option),
                });
+               let mut decode_update_add_htlcs = decode_update_add_htlcs.unwrap_or_else(|| new_hash_map());
                if fake_scid_rand_bytes.is_none() {
                        fake_scid_rand_bytes = Some(args.entropy_source.get_secure_random_bytes());
                }
@@ -10887,7 +11876,7 @@ where
                        let peer_state = &mut *peer_state_lock;
                        for phase in peer_state.channel_by_id.values() {
                                if let ChannelPhase::Funded(chan) = phase {
-                                       let logger = WithChannelContext::from(&args.logger, &chan.context);
+                                       let logger = WithChannelContext::from(&args.logger, &chan.context, None);
 
                                        // Channels that were persisted have to be funded, otherwise they should have been
                                        // discarded.
@@ -10903,7 +11892,7 @@ where
                                                }
                                        }
                                        if chan.get_latest_unblocked_monitor_update_id() > max_in_flight_update_id {
-                                               // If the channel is ahead of the monitor, return InvalidValue:
+                                               // If the channel is ahead of the monitor, return DangerousValue:
                                                log_error!(logger, "A ChannelMonitor is stale compared to the current ChannelManager! This indicates a potentially-critical violation of the chain::Watch API!");
                                                log_error!(logger, " The ChannelMonitor for channel {} is at update_id {} with update_id through {} in-flight",
                                                        chan.context.channel_id(), monitor.get_latest_update_id(), max_in_flight_update_id);
@@ -10912,7 +11901,7 @@ where
                                                log_error!(logger, " client applications must ensure that ChannelMonitor data is always available and the latest to avoid funds loss!");
                                                log_error!(logger, " Without the latest ChannelMonitor we cannot continue without risking funds.");
                                                log_error!(logger, " Please ensure the chain::Watch API requirements are met and file a bug report at https://github.com/lightningdevkit/rust-lightning");
-                                               return Err(DecodeError::InvalidValue);
+                                               return Err(DecodeError::DangerousValue);
                                        }
                                } else {
                                        // We shouldn't have persisted (or read) any unfunded channel types so none should have been
@@ -10926,7 +11915,7 @@ where
                if let Some(in_flight_upds) = in_flight_monitor_updates {
                        for ((counterparty_id, funding_txo), mut chan_in_flight_updates) in in_flight_upds {
                                let channel_id = funding_txo_to_channel_id.get(&funding_txo).copied();
-                               let logger = WithContext::from(&args.logger, Some(counterparty_id), channel_id);
+                               let logger = WithContext::from(&args.logger, Some(counterparty_id), channel_id, None);
                                if let Some(monitor) = args.channel_monitors.get(&funding_txo) {
                                        // Now that we've removed all the in-flight monitor updates for channels that are
                                        // still open, we need to replay any monitor updates that are for closed channels,
@@ -10945,6 +11934,7 @@ where
                                        log_error!(logger, " client applications must ensure that ChannelMonitor data is always available and the latest to avoid funds loss!");
                                        log_error!(logger, " Without the latest ChannelMonitor we cannot continue without risking funds.");
                                        log_error!(logger, " Please ensure the chain::Watch API requirements are met and file a bug report at https://github.com/lightningdevkit/rust-lightning");
+                                       log_error!(logger, " Pending in-flight updates are: {:?}", chan_in_flight_updates);
                                        return Err(DecodeError::InvalidValue);
                                }
                        }
@@ -10970,8 +11960,8 @@ where
                        for (_, monitor) in args.channel_monitors.iter() {
                                let counterparty_opt = outpoint_to_peer.get(&monitor.get_funding_txo().0);
                                if counterparty_opt.is_none() {
-                                       let logger = WithChannelMonitor::from(&args.logger, monitor);
                                        for (htlc_source, (htlc, _)) in monitor.get_pending_or_resolved_outbound_htlcs() {
+                                               let logger = WithChannelMonitor::from(&args.logger, monitor, Some(htlc.payment_hash));
                                                if let HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } = htlc_source {
                                                        if path.hops.is_empty() {
                                                                log_error!(logger, "Got an empty path for a pending payment");
@@ -11012,6 +12002,7 @@ where
                                                }
                                        }
                                        for (htlc_source, (htlc, preimage_opt)) in monitor.get_all_current_outbound_htlcs() {
+                                               let logger = WithChannelMonitor::from(&args.logger, monitor, Some(htlc.payment_hash));
                                                match htlc_source {
                                                        HTLCSource::PreviousHopData(prev_hop_data) => {
                                                                let pending_forward_matches_htlc = |info: &PendingAddHTLCInfo| {
@@ -11023,6 +12014,18 @@ where
                                                                // still have an entry for this HTLC in `forward_htlcs` or
                                                                // `pending_intercepted_htlcs`, we were apparently not persisted after
                                                                // the monitor was when forwarding the payment.
+                                                               decode_update_add_htlcs.retain(|scid, update_add_htlcs| {
+                                                                       update_add_htlcs.retain(|update_add_htlc| {
+                                                                               let matches = *scid == prev_hop_data.short_channel_id &&
+                                                                                       update_add_htlc.htlc_id == prev_hop_data.htlc_id;
+                                                                               if matches {
+                                                                                       log_info!(logger, "Removing pending to-decode HTLC with hash {} as it was forwarded to the closed channel {}",
+                                                                                               &htlc.payment_hash, &monitor.channel_id());
+                                                                               }
+                                                                               !matches
+                                                                       });
+                                                                       !update_add_htlcs.is_empty()
+                                                               });
                                                                forward_htlcs.retain(|_, forwards| {
                                                                        forwards.retain(|forward| {
                                                                                if let HTLCForwardInfo::AddHTLC(htlc_info) = forward {
@@ -11104,7 +12107,7 @@ where
                        }
                }
 
-               if !forward_htlcs.is_empty() || pending_outbounds.needs_abandon() {
+               if !forward_htlcs.is_empty() || !decode_update_add_htlcs.is_empty() || pending_outbounds.needs_abandon() {
                        // If we have pending HTLCs to forward, assume we either dropped a
                        // `PendingHTLCsForwardable` or the user received it but never processed it as they
                        // shut down before the timer hit. Either way, set the time_forwardable to a small
@@ -11153,7 +12156,7 @@ where
                                let purpose = match &htlcs[0].onion_payload {
                                        OnionPayload::Invoice { _legacy_hop_data } => {
                                                if let Some(hop_data) = _legacy_hop_data {
-                                                       events::PaymentPurpose::InvoicePayment {
+                                                       events::PaymentPurpose::Bolt11InvoicePayment {
                                                                payment_preimage: match pending_inbound_payments.get(&payment_hash) {
                                                                        Some(inbound_payment) => inbound_payment.payment_preimage,
                                                                        None => match inbound_payment::verify(payment_hash, &hop_data, 0, &expanded_inbound_key, &args.logger) {
@@ -11197,7 +12200,7 @@ where
                        let peer_state = &mut *peer_state_lock;
                        for (chan_id, phase) in peer_state.channel_by_id.iter_mut() {
                                if let ChannelPhase::Funded(chan) = phase {
-                                       let logger = WithChannelContext::from(&args.logger, &chan.context);
+                                       let logger = WithChannelContext::from(&args.logger, &chan.context, None);
                                        if chan.context.outbound_scid_alias() == 0 {
                                                let mut outbound_scid_alias;
                                                loop {
@@ -11267,7 +12270,7 @@ where
                                                        let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                                                        let peer_state = &mut *peer_state_lock;
                                                        if let Some(ChannelPhase::Funded(channel)) = peer_state.channel_by_id.get_mut(&previous_channel_id) {
-                                                               let logger = WithChannelContext::from(&args.logger, &channel.context);
+                                                               let logger = WithChannelContext::from(&args.logger, &channel.context, Some(payment_hash));
                                                                channel.claim_htlc_while_disconnected_dropping_mon_update(claimable_htlc.prev_hop.htlc_id, payment_preimage, &&logger);
                                                        }
                                                }
@@ -11282,6 +12285,7 @@ where
                                                amount_msat: claimable_amt_msat,
                                                htlcs: payment.htlcs.iter().map(events::ClaimedHTLC::from).collect(),
                                                sender_intended_total_msat: payment.htlcs.first().map(|htlc| htlc.total_msat),
+                                               onion_fields: payment.onion_fields,
                                        }, None));
                                }
                        }
@@ -11290,7 +12294,7 @@ where
                for (node_id, monitor_update_blocked_actions) in monitor_update_blocked_actions_per_peer.unwrap() {
                        if let Some(peer_state) = per_peer_state.get(&node_id) {
                                for (channel_id, actions) in monitor_update_blocked_actions.iter() {
-                                       let logger = WithContext::from(&args.logger, Some(node_id), Some(*channel_id));
+                                       let logger = WithContext::from(&args.logger, Some(node_id), Some(*channel_id), None);
                                        for action in actions.iter() {
                                                if let MonitorUpdateCompletionAction::EmitEventAndFreeOtherChannel {
                                                        downstream_counterparty_and_funding_outpoint:
@@ -11318,7 +12322,7 @@ where
                                }
                                peer_state.lock().unwrap().monitor_update_blocked_actions = monitor_update_blocked_actions;
                        } else {
-                               log_error!(WithContext::from(&args.logger, Some(node_id), None), "Got blocked actions without a per-peer-state for {}", node_id);
+                               log_error!(WithContext::from(&args.logger, Some(node_id), None, None), "Got blocked actions without a per-peer-state for {}", node_id);
                                return Err(DecodeError::InvalidValue);
                        }
                }
@@ -11338,6 +12342,7 @@ where
                        pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),
 
                        forward_htlcs: Mutex::new(forward_htlcs),
+                       decode_update_add_htlcs: Mutex::new(decode_update_add_htlcs),
                        claimable_payments: Mutex::new(ClaimablePayments { claimable_payments, pending_claiming_payments: pending_claiming_payments.unwrap() }),
                        outbound_scid_aliases: Mutex::new(outbound_scid_aliases),
                        outpoint_to_peer: Mutex::new(outpoint_to_peer),
@@ -11366,6 +12371,8 @@ where
 
                        pending_offers_messages: Mutex::new(Vec::new()),
 
+                       pending_broadcast_messages: Mutex::new(Vec::new()),
+
                        entropy_source: args.entropy_source,
                        node_signer: args.node_signer,
                        signer_provider: args.signer_provider,
@@ -11386,7 +12393,9 @@ where
                        // don't remember in the `ChannelMonitor` where we got a preimage from, but if the
                        // channel is closed we just assume that it probably came from an on-chain claim.
                        channel_manager.claim_funds_internal(source, preimage, Some(downstream_value), None,
-                               downstream_closed, true, downstream_node_id, downstream_funding, downstream_channel_id);
+                               downstream_closed, true, downstream_node_id, downstream_funding,
+                               downstream_channel_id, None
+                       );
                }
 
                //TODO: Broadcast channel update for closed channels, but only after we've made a
@@ -11403,8 +12412,7 @@ mod tests {
        use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
        use core::sync::atomic::Ordering;
        use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
-       use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
-       use crate::ln::ChannelId;
+       use crate::ln::types::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
        use crate::ln::channelmanager::{create_recv_pending_htlc_info, HTLCForwardInfo, inbound_payment, PaymentId, PaymentSendFailure, RecipientOnionFields, InterceptId};
        use crate::ln::functional_test_utils::*;
        use crate::ln::msgs::{self, ErrorAction};
@@ -11895,6 +12903,61 @@ mod tests {
                }
        }
 
+       #[test]
+       fn test_channel_update_cached() {
+               let chanmon_cfgs = create_chanmon_cfgs(3);
+               let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+               let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+               let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+               let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
+
+               nodes[0].node.force_close_channel_with_peer(&chan.2, &nodes[1].node.get_our_node_id(), None, true).unwrap();
+               check_added_monitors!(nodes[0], 1);
+               check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed, [nodes[1].node.get_our_node_id()], 100000);
+
+               // Confirm that the channel_update was not sent immediately to node[1] but was cached.
+               let node_1_events = nodes[1].node.get_and_clear_pending_msg_events();
+               assert_eq!(node_1_events.len(), 0);
+
+               {
+                       // Assert that ChannelUpdate message has been added to node[0] pending broadcast messages
+                       let pending_broadcast_messages= nodes[0].node.pending_broadcast_messages.lock().unwrap();
+                       assert_eq!(pending_broadcast_messages.len(), 1);
+               }
+
+               // Test that we do not retrieve the pending broadcast messages when we are not connected to any peer
+               nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
+               nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
+
+               nodes[0].node.peer_disconnected(&nodes[2].node.get_our_node_id());
+               nodes[2].node.peer_disconnected(&nodes[0].node.get_our_node_id());
+
+               let node_0_events = nodes[0].node.get_and_clear_pending_msg_events();
+               assert_eq!(node_0_events.len(), 0);
+
+               // Now we reconnect to a peer
+               nodes[0].node.peer_connected(&nodes[2].node.get_our_node_id(), &msgs::Init {
+                       features: nodes[2].node.init_features(), networks: None, remote_network_address: None
+               }, true).unwrap();
+               nodes[2].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init {
+                       features: nodes[0].node.init_features(), networks: None, remote_network_address: None
+               }, false).unwrap();
+
+               // Confirm that get_and_clear_pending_msg_events correctly captures pending broadcast messages
+               let node_0_events = nodes[0].node.get_and_clear_pending_msg_events();
+               assert_eq!(node_0_events.len(), 1);
+               match &node_0_events[0] {
+                       MessageSendEvent::BroadcastChannelUpdate { .. } => (),
+                       _ => panic!("Unexpected event"),
+               }
+               {
+                       // Assert that ChannelUpdate message has been cleared from nodes[0] pending broadcast messages
+                       let pending_broadcast_messages= nodes[0].node.pending_broadcast_messages.lock().unwrap();
+                       assert_eq!(pending_broadcast_messages.len(), 0);
+               }
+       }
+
        #[test]
        fn test_drop_disconnected_peers_when_removing_channels() {
                let chanmon_cfgs = create_chanmon_cfgs(2);
@@ -12761,10 +13824,12 @@ pub mod bench {
        use crate::util::test_utils;
        use crate::util::config::{UserConfig, MaxDustHTLCExposure};
 
+       use bitcoin::amount::Amount;
        use bitcoin::blockdata::locktime::absolute::LockTime;
        use bitcoin::hashes::Hash;
        use bitcoin::hashes::sha256::Hash as Sha256;
        use bitcoin::{Transaction, TxOut};
+       use bitcoin::transaction::Version;
 
        use crate::sync::{Arc, Mutex, RwLock};
 
@@ -12841,8 +13906,8 @@ pub mod bench {
 
                let tx;
                if let Event::FundingGenerationReady { temporary_channel_id, output_script, .. } = get_event!(node_a_holder, Event::FundingGenerationReady) {
-                       tx = Transaction { version: 2, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
-                               value: 8_000_000, script_pubkey: output_script,
+                       tx = Transaction { version: Version::TWO, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
+                               value: Amount::from_sat(8_000_000), script_pubkey: output_script,
                        }]};
                        node_a.funding_transaction_generated(&temporary_channel_id, &node_b.get_our_node_id(), tx.clone()).unwrap();
                } else { panic!(); }
index 691f858b4590a2b6d62f06e33c7dbdf4fb10ba8e..0cb66fa512839f1ac899b4a7c09158580104d5d0 100644 (file)
@@ -66,6 +66,8 @@
 //!      for more info).
 //! - `Keysend` - send funds to a node without an invoice
 //!     (see the [`Keysend` feature assignment proposal](https://github.com/lightning/bolts/issues/605#issuecomment-606679798) for more information).
+//! - `Trampoline` - supports receiving and forwarding Trampoline payments
+//!     (see the [`Trampoline` feature proposal](https://github.com/lightning/bolts/pull/836) for more information).
 //!
 //! LDK knows about the following features, but does not support them:
 //! - `AnchorsNonzeroFeeHtlcTx` - the initial version of anchor outputs, which was later found to be
 //! [BOLT #9]: https://github.com/lightning/bolts/blob/master/09-features.md
 //! [messages]: crate::ln::msgs
 
-use crate::{io, io_extras};
+#[allow(unused_imports)]
 use crate::prelude::*;
+
+use crate::{io, io_extras};
 use core::{cmp, fmt};
 use core::borrow::Borrow;
 use core::hash::{Hash, Hasher};
 use core::marker::PhantomData;
 
-use bitcoin::bech32;
-use bitcoin::bech32::{Base32Len, FromBase32, ToBase32, u5, WriteBase32};
+use bech32::{Base32Len, FromBase32, ToBase32, u5, WriteBase32};
 use crate::ln::msgs::DecodeError;
 use crate::util::ser::{Readable, WithoutLength, Writeable, Writer};
 
 mod sealed {
+       #[allow(unused_imports)]
        use crate::prelude::*;
        use crate::ln::features::Features;
 
@@ -152,6 +156,8 @@ mod sealed {
                ChannelType | SCIDPrivacy,
                // Byte 6
                ZeroConf,
+               // Byte 7
+               Trampoline,
        ]);
        define_context!(NodeContext, [
                // Byte 0
@@ -168,6 +174,8 @@ mod sealed {
                ChannelType | SCIDPrivacy,
                // Byte 6
                ZeroConf | Keysend,
+               // Byte 7
+               Trampoline,
        ]);
        define_context!(ChannelContext, []);
        define_context!(Bolt11InvoiceContext, [
@@ -185,6 +193,8 @@ mod sealed {
                ,
                // Byte 6
                PaymentMetadata,
+               // Byte 7
+               Trampoline,
        ]);
        define_context!(OfferContext, []);
        define_context!(InvoiceRequestContext, []);
@@ -420,6 +430,9 @@ mod sealed {
        define_feature!(55, Keysend, [NodeContext],
                "Feature flags for keysend payments.", set_keysend_optional, set_keysend_required,
                supports_keysend, requires_keysend);
+       define_feature!(57, Trampoline, [InitContext, NodeContext, Bolt11InvoiceContext],
+               "Feature flags for Trampoline routing.", set_trampoline_routing_optional, set_trampoline_routing_required,
+               supports_trampoline_routing, requires_trampoline_routing);
        // Note: update the module-level docs when a new feature bit is added!
 
        #[cfg(test)]
@@ -1014,7 +1027,7 @@ impl<T: sealed::Context> Readable for WithoutLength<Features<T>> {
 #[cfg(test)]
 mod tests {
        use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, Bolt11InvoiceFeatures, NodeFeatures, OfferFeatures, sealed};
-       use bitcoin::bech32::{Base32Len, FromBase32, ToBase32, u5};
+       use bech32::{Base32Len, FromBase32, ToBase32, u5};
        use crate::util::ser::{Readable, WithoutLength, Writeable};
 
        #[test]
index cb310ec95398a38309840a3177966102da3eeebb..c0f3458b3d6246969f3525123ec3cd4d302f7031 100644 (file)
@@ -15,7 +15,7 @@ use crate::chain::channelmonitor::ChannelMonitor;
 use crate::chain::transaction::OutPoint;
 use crate::events::{ClaimedHTLC, ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, PaymentFailureReason};
 use crate::events::bump_transaction::{BumpTransactionEvent, BumpTransactionEventHandler, Wallet, WalletSource};
-use crate::ln::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
+use crate::ln::types::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
 use crate::ln::channelmanager::{AChannelManager, ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, PaymentId, MIN_CLTV_EXPIRY_DELTA};
 use crate::ln::features::InitFeatures;
 use crate::ln::msgs;
@@ -35,15 +35,17 @@ use crate::util::test_utils;
 use crate::util::test_utils::{panicking, TestChainMonitor, TestScorer, TestKeysInterface};
 use crate::util::ser::{ReadableArgs, Writeable};
 
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::block::{Block, Header, Version};
 use bitcoin::blockdata::locktime::absolute::LockTime;
 use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut};
 use bitcoin::hash_types::{BlockHash, TxMerkleNode};
 use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::hashes::Hash as _;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 use bitcoin::pow::CompactTarget;
 use bitcoin::secp256k1::{PublicKey, SecretKey};
+use bitcoin::transaction;
 
 use alloc::rc::Rc;
 use core::cell::RefCell;
@@ -95,7 +97,7 @@ pub fn mine_transaction_without_consistency_checks<'a, 'b, 'c, 'd>(node: &'a Nod
                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: LockTime::ZERO, input: Vec::new(), output: Vec::new() });
+               block.txdata.push(Transaction { version: transaction::Version(0), lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() });
        }
        block.txdata.push((*tx).clone());
        do_connect_block_without_consistency_checks(node, block, false);
@@ -113,7 +115,7 @@ pub fn confirm_transactions_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, txn:
        }
        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
-               txdata.push(Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() });
+               txdata.push(Transaction { version: transaction::Version(0), lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() });
        }
        for tx in txn {
                txdata.push((*tx).clone());
@@ -415,6 +417,7 @@ type TestOnionMessenger<'chan_man, 'node_cfg, 'chan_mon_cfg> = OnionMessenger<
        DedicatedEntropy,
        &'node_cfg test_utils::TestKeysInterface,
        &'chan_mon_cfg test_utils::TestLogger,
+       &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>,
        &'node_cfg test_utils::TestMessageRouter<'chan_mon_cfg>,
        &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>,
        IgnoringMessageHandler,
@@ -1154,8 +1157,8 @@ fn internal_create_funding_transaction<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>,
                                Vec::new()
                        };
 
-                       let tx = Transaction { version: chan_id as i32, lock_time: LockTime::ZERO, input, output: vec![TxOut {
-                               value: *channel_value_satoshis, script_pubkey: output_script.clone(),
+                       let tx = Transaction { version: transaction::Version(chan_id as i32), lock_time: LockTime::ZERO, input, output: vec![TxOut {
+                               value: Amount::from_sat(*channel_value_satoshis), script_pubkey: output_script.clone(),
                        }]};
                        let funding_outpoint = OutPoint { txid: tx.txid(), index: 0 };
                        (*temporary_channel_id, tx, funding_outpoint)
@@ -1475,15 +1478,15 @@ pub fn update_nodes_with_chan_announce<'a, 'b, 'c, 'd>(nodes: &'a Vec<Node<'b, '
 
 pub fn do_check_spends<F: Fn(&bitcoin::blockdata::transaction::OutPoint) -> Option<TxOut>>(tx: &Transaction, get_output: F) {
        for outp in tx.output.iter() {
-               assert!(outp.value >= outp.script_pubkey.dust_value().to_sat(), "Spending tx output didn't meet dust limit");
+               assert!(outp.value >= outp.script_pubkey.dust_value(), "Spending tx output didn't meet dust limit");
        }
        let mut total_value_in = 0;
        for input in tx.input.iter() {
-               total_value_in += get_output(&input.previous_output).unwrap().value;
+               total_value_in += get_output(&input.previous_output).unwrap().value.to_sat();
        }
        let mut total_value_out = 0;
        for output in tx.output.iter() {
-               total_value_out += output.value;
+               total_value_out += output.value.to_sat();
        }
        let min_fee = (tx.weight().to_wu() as u64 + 3) / 4; // One sat per vbyte (ie per weight/4, rounded up)
        // Input amount - output amount = fee, so check that out + min_fee is smaller than input
@@ -1497,7 +1500,7 @@ macro_rules! check_spends {
                {
                        $(
                        for outp in $spends_txn.output.iter() {
-                               assert!(outp.value >= outp.script_pubkey.dust_value().to_sat(), "Input tx output didn't meet dust limit");
+                               assert!(outp.value >= outp.script_pubkey.dust_value(), "Input tx output didn't meet dust limit");
                        }
                        )*
                        let get_output = |out_point: &bitcoin::blockdata::transaction::OutPoint| {
@@ -1553,11 +1556,29 @@ macro_rules! check_warn_msg {
        }}
 }
 
+/// Checks if at least one peer is connected.
+fn is_any_peer_connected(node: &Node) -> bool {
+       let peer_state = node.node.per_peer_state.read().unwrap();
+       for (_, peer_mutex) in peer_state.iter() {
+               let peer = peer_mutex.lock().unwrap();
+               if peer.is_connected { return true; }
+       }
+       false
+}
+
 /// Check that a channel's closing channel update has been broadcasted, and optionally
 /// check whether an error message event has occurred.
 pub fn check_closed_broadcast(node: &Node, num_channels: usize, with_error_msg: bool) -> Vec<msgs::ErrorMessage> {
+       let mut dummy_connected = false;
+       if !is_any_peer_connected(node) {
+               connect_dummy_node(&node);
+               dummy_connected = true;
+       }
        let msg_events = node.node.get_and_clear_pending_msg_events();
        assert_eq!(msg_events.len(), if with_error_msg { num_channels * 2 } else { num_channels });
+       if dummy_connected {
+               disconnect_dummy_node(&node);
+       }
        msg_events.into_iter().filter_map(|msg_event| {
                match msg_event {
                        MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
@@ -1827,14 +1848,9 @@ macro_rules! expect_htlc_handling_failed_destinations {
 /// there are any [`Event::HTLCHandlingFailed`] events their [`HTLCDestination`] is included in the
 /// `expected_failures` set.
 pub fn expect_pending_htlcs_forwardable_conditions(events: Vec<Event>, expected_failures: &[HTLCDestination]) {
-       match events[0] {
-               Event::PendingHTLCsForwardable { .. } => { },
-               _ => panic!("Unexpected event {:?}", events),
-       };
-
        let count = expected_failures.len() + 1;
        assert_eq!(events.len(), count);
-
+       assert!(events.iter().find(|event| matches!(event, Event::PendingHTLCsForwardable { .. })).is_some());
        if expected_failures.len() > 0 {
                expect_htlc_handling_failed_destinations!(events, expected_failures)
        }
@@ -2051,7 +2067,7 @@ macro_rules! get_payment_preimage_hash {
 /// Gets a route from the given sender to the node described in `payment_params`.
 pub fn get_route(send_node: &Node, route_params: &RouteParameters) -> Result<Route, msgs::LightningError> {
        let scorer = TestScorer::new();
-       let keys_manager = TestKeysInterface::new(&[0u8; 32], bitcoin::network::constants::Network::Testnet);
+       let keys_manager = TestKeysInterface::new(&[0u8; 32], Network::Testnet);
        let random_seed_bytes = keys_manager.get_secure_random_bytes();
        router::get_route(
                &send_node.node.get_our_node_id(), route_params, &send_node.network_graph.read_only(),
@@ -2063,7 +2079,7 @@ pub fn get_route(send_node: &Node, route_params: &RouteParameters) -> Result<Rou
 /// Like `get_route` above, but adds a random CLTV offset to the final hop.
 pub fn find_route(send_node: &Node, route_params: &RouteParameters) -> Result<Route, msgs::LightningError> {
        let scorer = TestScorer::new();
-       let keys_manager = TestKeysInterface::new(&[0u8; 32], bitcoin::network::constants::Network::Testnet);
+       let keys_manager = TestKeysInterface::new(&[0u8; 32], Network::Testnet);
        let random_seed_bytes = keys_manager.get_secure_random_bytes();
        router::find_route(
                &send_node.node.get_our_node_id(), route_params, &send_node.network_graph,
@@ -2115,7 +2131,15 @@ pub fn check_payment_claimable(
                        assert_eq!(expected_recv_value, *amount_msat);
                        assert_eq!(expected_receiver_node_id, receiver_node_id.unwrap());
                        match purpose {
-                               PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
+                               PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
+                                       assert_eq!(&expected_payment_preimage, payment_preimage);
+                                       assert_eq!(expected_payment_secret, *payment_secret);
+                               },
+                               PaymentPurpose::Bolt12OfferPayment { payment_preimage, payment_secret, .. } => {
+                                       assert_eq!(&expected_payment_preimage, payment_preimage);
+                                       assert_eq!(expected_payment_secret, *payment_secret);
+                               },
+                               PaymentPurpose::Bolt12RefundPayment { payment_preimage, payment_secret, .. } => {
                                        assert_eq!(&expected_payment_preimage, payment_preimage);
                                        assert_eq!(expected_payment_secret, *payment_secret);
                                },
@@ -2231,8 +2255,8 @@ pub fn expect_payment_forwarded<CM: AChannelManager, H: NodeHolder<CM=CM>>(
 ) -> Option<u64> {
        match event {
                Event::PaymentForwarded {
-                       total_fee_earned_msat, prev_channel_id, claim_from_onchain_tx, next_channel_id,
-                       outbound_amount_forwarded_msat: _, skimmed_fee_msat
+                       prev_channel_id, next_channel_id, prev_user_channel_id, next_user_channel_id,
+                       total_fee_earned_msat, skimmed_fee_msat, claim_from_onchain_tx, ..
                } => {
                        if allow_1_msat_fee_overpay {
                                // Aggregating fees for blinded paths may result in a rounding error, causing slight
@@ -2249,12 +2273,31 @@ pub fn expect_payment_forwarded<CM: AChannelManager, H: NodeHolder<CM=CM>>(
                        assert!(skimmed_fee_msat == expected_extra_fees_msat);
                        if !upstream_force_closed {
                                // Is the event prev_channel_id in one of the channels between the two nodes?
-                               assert!(node.node().list_channels().iter().any(|x| x.counterparty.node_id == prev_node.node().get_our_node_id() && x.channel_id == prev_channel_id.unwrap()));
+                               assert!(node.node().list_channels().iter().any(|x|
+                                       x.counterparty.node_id == prev_node.node().get_our_node_id() &&
+                                       x.channel_id == prev_channel_id.unwrap() &&
+                                       x.user_channel_id == prev_user_channel_id.unwrap()
+                               ));
                        }
                        // We check for force closures since a force closed channel is removed from the
                        // node's channel list
                        if !downstream_force_closed {
-                               assert!(node.node().list_channels().iter().any(|x| x.counterparty.node_id == next_node.node().get_our_node_id() && x.channel_id == next_channel_id.unwrap()));
+                               // As documented, `next_user_channel_id` will only be `Some` if we didn't settle via an
+                               // onchain transaction, just as the `total_fee_earned_msat` field. Rather than
+                               // introducing yet another variable, we use the latter's state as a flag to detect
+                               // this and only check if it's `Some`.
+                               if total_fee_earned_msat.is_none() {
+                                       assert!(node.node().list_channels().iter().any(|x|
+                                               x.counterparty.node_id == next_node.node().get_our_node_id() &&
+                                               x.channel_id == next_channel_id.unwrap()
+                                       ));
+                               } else {
+                                       assert!(node.node().list_channels().iter().any(|x|
+                                               x.counterparty.node_id == next_node.node().get_our_node_id() &&
+                                               x.channel_id == next_channel_id.unwrap() &&
+                                               x.user_channel_id == next_user_channel_id.unwrap()
+                                       ));
+                               }
                        }
                        assert_eq!(claim_from_onchain_tx, downstream_force_closed);
                        total_fee_earned_msat
@@ -2493,6 +2536,8 @@ pub struct PassAlongPathArgs<'a, 'b, 'c, 'd> {
        pub clear_recipient_events: bool,
        pub expected_preimage: Option<PaymentPreimage>,
        pub is_probe: bool,
+       pub custom_tlvs: Vec<(u64, Vec<u8>)>,
+       pub payment_metadata: Option<Vec<u8>>,
 }
 
 impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> {
@@ -2503,7 +2548,7 @@ impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> {
                Self {
                        origin_node, expected_path, recv_value, payment_hash, payment_secret: None, event,
                        payment_claimable_expected: true, clear_recipient_events: true, expected_preimage: None,
-                       is_probe: false,
+                       is_probe: false, custom_tlvs: Vec::new(), payment_metadata: None,
                }
        }
        pub fn without_clearing_recipient_events(mut self) -> Self {
@@ -2527,13 +2572,21 @@ impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> {
                self.expected_preimage = Some(payment_preimage);
                self
        }
+       pub fn with_custom_tlvs(mut self, custom_tlvs: Vec<(u64, Vec<u8>)>) -> Self {
+               self.custom_tlvs = custom_tlvs;
+               self
+       }
+       pub fn with_payment_metadata(mut self, payment_metadata: Vec<u8>) -> Self {
+               self.payment_metadata = Some(payment_metadata);
+               self
+       }
 }
 
 pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option<Event> {
        let PassAlongPathArgs {
                origin_node, expected_path, recv_value, payment_hash: our_payment_hash,
                payment_secret: our_payment_secret, event: ev, payment_claimable_expected,
-               clear_recipient_events, expected_preimage, is_probe
+               clear_recipient_events, expected_preimage, is_probe, custom_tlvs, payment_metadata,
        } = args;
 
        let mut payment_event = SendEvent::from_event(ev);
@@ -2566,8 +2619,20 @@ pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option<Event>
                                                assert_eq!(our_payment_hash, *payment_hash);
                                                assert_eq!(node.node.get_our_node_id(), receiver_node_id.unwrap());
                                                assert!(onion_fields.is_some());
+                                               assert_eq!(onion_fields.as_ref().unwrap().custom_tlvs, custom_tlvs);
+                                               assert_eq!(onion_fields.as_ref().unwrap().payment_metadata, payment_metadata);
                                                match &purpose {
-                                                       PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
+                                                       PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
+                                                               assert_eq!(expected_preimage, *payment_preimage);
+                                                               assert_eq!(our_payment_secret.unwrap(), *payment_secret);
+                                                               assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret);
+                                                       },
+                                                       PaymentPurpose::Bolt12OfferPayment { payment_preimage, payment_secret, .. } => {
+                                                               assert_eq!(expected_preimage, *payment_preimage);
+                                                               assert_eq!(our_payment_secret.unwrap(), *payment_secret);
+                                                               assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret);
+                                                       },
+                                                       PaymentPurpose::Bolt12RefundPayment { payment_preimage, payment_secret, .. } => {
                                                                assert_eq!(expected_preimage, *payment_preimage);
                                                                assert_eq!(our_payment_secret.unwrap(), *payment_secret);
                                                                assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret);
@@ -2653,18 +2718,12 @@ pub fn send_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, route: Route
        (our_payment_preimage, our_payment_hash, our_payment_secret, payment_id)
 }
 
-pub fn do_claim_payment_along_route<'a, 'b, 'c>(
-       origin_node: &Node<'a, 'b, 'c>, expected_paths: &[&[&Node<'a, 'b, 'c>]], skip_last: bool,
-       our_payment_preimage: PaymentPreimage
-) -> u64 {
-       for path in expected_paths.iter() {
-               assert_eq!(path.last().unwrap().node.get_our_node_id(), expected_paths[0].last().unwrap().node.get_our_node_id());
+pub fn do_claim_payment_along_route(args: ClaimAlongRouteArgs) -> u64 {
+       for path in args.expected_paths.iter() {
+               assert_eq!(path.last().unwrap().node.get_our_node_id(), args.expected_paths[0].last().unwrap().node.get_our_node_id());
        }
-       expected_paths[0].last().unwrap().node.claim_funds(our_payment_preimage);
-       pass_claimed_payment_along_route(
-               ClaimAlongRouteArgs::new(origin_node, expected_paths, our_payment_preimage)
-                       .skip_last(skip_last)
-       )
+       args.expected_paths[0].last().unwrap().node.claim_funds(args.payment_preimage);
+       pass_claimed_payment_along_route(args)
 }
 
 pub struct ClaimAlongRouteArgs<'a, 'b, 'c, 'd> {
@@ -2674,6 +2733,7 @@ pub struct ClaimAlongRouteArgs<'a, 'b, 'c, 'd> {
        pub expected_min_htlc_overpay: Vec<u32>,
        pub skip_last: bool,
        pub payment_preimage: PaymentPreimage,
+       pub custom_tlvs: Vec<(u64, Vec<u8>)>,
        // Allow forwarding nodes to have taken 1 msat more fee than expected based on the downstream
        // fulfill amount.
        //
@@ -2692,7 +2752,7 @@ impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> {
                Self {
                        origin_node, expected_paths, expected_extra_fees: vec![0; expected_paths.len()],
                        expected_min_htlc_overpay: vec![0; expected_paths.len()], skip_last: false, payment_preimage,
-                       allow_1_msat_fee_overpay: false,
+                       allow_1_msat_fee_overpay: false, custom_tlvs: vec![],
                }
        }
        pub fn skip_last(mut self, skip_last: bool) -> Self {
@@ -2711,12 +2771,16 @@ impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> {
                self.allow_1_msat_fee_overpay = true;
                self
        }
+       pub fn with_custom_tlvs(mut self, custom_tlvs: Vec<(u64, Vec<u8>)>) -> Self {
+               self.custom_tlvs = custom_tlvs;
+               self
+       }
 }
 
-pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArgs) -> u64 {
+pub fn pass_claimed_payment_along_route(args: ClaimAlongRouteArgs) -> u64 {
        let ClaimAlongRouteArgs {
                origin_node, expected_paths, expected_extra_fees, expected_min_htlc_overpay, skip_last,
-               payment_preimage: our_payment_preimage, allow_1_msat_fee_overpay,
+               payment_preimage: our_payment_preimage, allow_1_msat_fee_overpay, custom_tlvs,
        } = args;
        let claim_event = expected_paths[0].last().unwrap().node.get_and_clear_pending_events();
        assert_eq!(claim_event.len(), 1);
@@ -2724,32 +2788,36 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg
        let mut fwd_amt_msat = 0;
        match claim_event[0] {
                Event::PaymentClaimed {
-                       purpose: PaymentPurpose::SpontaneousPayment(preimage),
+                       purpose: PaymentPurpose::SpontaneousPayment(preimage)
+                               | PaymentPurpose::Bolt11InvoicePayment { payment_preimage: Some(preimage), .. }
+                               | PaymentPurpose::Bolt12OfferPayment { payment_preimage: Some(preimage), .. }
+                               | PaymentPurpose::Bolt12RefundPayment { payment_preimage: Some(preimage), .. },
                        amount_msat,
                        ref htlcs,
-                       .. }
-               | Event::PaymentClaimed {
-                       purpose: PaymentPurpose::InvoicePayment { payment_preimage: Some(preimage), ..},
-                       ref htlcs,
-                       amount_msat,
+                       ref onion_fields,
                        ..
                } => {
                        assert_eq!(preimage, our_payment_preimage);
                        assert_eq!(htlcs.len(), expected_paths.len());  // One per path.
                        assert_eq!(htlcs.iter().map(|h| h.value_msat).sum::<u64>(), amount_msat);
+                       assert_eq!(onion_fields.as_ref().unwrap().custom_tlvs, custom_tlvs);
                        expected_paths.iter().zip(htlcs).for_each(|(path, htlc)| check_claimed_htlc_channel(origin_node, path, htlc));
                        fwd_amt_msat = amount_msat;
                },
                Event::PaymentClaimed {
-                       purpose: PaymentPurpose::InvoicePayment { .. },
+                       purpose: PaymentPurpose::Bolt11InvoicePayment { .. }
+                               | PaymentPurpose::Bolt12OfferPayment { .. }
+                               | PaymentPurpose::Bolt12RefundPayment { .. },
                        payment_hash,
                        amount_msat,
                        ref htlcs,
+                       ref onion_fields,
                        ..
                } => {
                        assert_eq!(&payment_hash.0, &Sha256::hash(&our_payment_preimage.0)[..]);
                        assert_eq!(htlcs.len(), expected_paths.len());  // One per path.
                        assert_eq!(htlcs.iter().map(|h| h.value_msat).sum::<u64>(), amount_msat);
+                       assert_eq!(onion_fields.as_ref().unwrap().custom_tlvs, custom_tlvs);
                        expected_paths.iter().zip(htlcs).for_each(|(path, htlc)| check_claimed_htlc_channel(origin_node, path, htlc));
                        fwd_amt_msat = amount_msat;
                }
@@ -2893,15 +2961,20 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg
 
        expected_total_fee_msat
 }
-pub fn claim_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_preimage: PaymentPreimage) {
-       let expected_total_fee_msat = do_claim_payment_along_route(origin_node, expected_paths, skip_last, our_payment_preimage);
+pub fn claim_payment_along_route(args: ClaimAlongRouteArgs) {
+       let origin_node = args.origin_node;
+       let payment_preimage = args.payment_preimage;
+       let skip_last = args.skip_last;
+       let expected_total_fee_msat = do_claim_payment_along_route(args);
        if !skip_last {
-               expect_payment_sent!(origin_node, our_payment_preimage, Some(expected_total_fee_msat));
+               expect_payment_sent!(origin_node, payment_preimage, Some(expected_total_fee_msat));
        }
 }
 
 pub fn claim_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], our_payment_preimage: PaymentPreimage) {
-       claim_payment_along_route(origin_node, &[expected_route], false, our_payment_preimage);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(origin_node, &[expected_route], our_payment_preimage)
+       );
 }
 
 pub const TEST_FINAL_CLTV: u32 = 70;
@@ -3161,8 +3234,8 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
        for i in 0..node_count {
                let dedicated_entropy = DedicatedEntropy(RandomBytes::new([i as u8; 32]));
                let onion_messenger = OnionMessenger::new(
-                       dedicated_entropy, cfgs[i].keys_manager, cfgs[i].logger, &cfgs[i].message_router,
-                       &chan_mgrs[i], IgnoringMessageHandler {},
+                       dedicated_entropy, cfgs[i].keys_manager, cfgs[i].logger, &chan_mgrs[i],
+                       &cfgs[i].message_router, &chan_mgrs[i], IgnoringMessageHandler {},
                );
                let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
                let wallet_source = Arc::new(test_utils::TestWalletSource::new(SecretKey::from_slice(&[i as u8 + 1; 32]).unwrap()));
@@ -3210,6 +3283,28 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
        nodes
 }
 
+pub fn connect_dummy_node<'a, 'b: 'a, 'c: 'b>(node: &Node<'a, 'b, 'c>) {
+       let node_id_dummy = PublicKey::from_slice(&[2; 33]).unwrap();
+
+       let mut dummy_init_features = InitFeatures::empty();
+       dummy_init_features.set_static_remote_key_required();
+
+       let init_dummy = msgs::Init {
+               features: dummy_init_features,
+               networks: None,
+               remote_network_address: None
+       };
+
+       node.node.peer_connected(&node_id_dummy, &init_dummy, true).unwrap();
+       node.onion_messenger.peer_connected(&node_id_dummy, &init_dummy, true).unwrap();
+}
+
+pub fn disconnect_dummy_node<'a, 'b: 'a, 'c: 'b>(node: &Node<'a, 'b, 'c>) {
+       let node_id_dummy = PublicKey::from_slice(&[2; 33]).unwrap();
+       node.node.peer_disconnected(&node_id_dummy);
+       node.onion_messenger.peer_disconnected(&node_id_dummy);
+}
+
 // Note that the following only works for CLTV values up to 128
 pub const ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 137; // Here we have a diff due to HTLC CLTV expiry being < 2^15 in test
 pub const ACCEPTED_HTLC_SCRIPT_WEIGHT_ANCHORS: usize = 140; // Here we have a diff due to HTLC CLTV expiry being < 2^15 in test
@@ -3321,15 +3416,21 @@ pub fn check_preimage_claim<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, prev_txn: &Vec<
 }
 
 pub fn handle_announce_close_broadcast_events<'a, 'b, 'c>(nodes: &Vec<Node<'a, 'b, 'c>>, a: usize, b: usize, needs_err_handle: bool, expected_error: &str)  {
+       let mut dummy_connected = false;
+       if !is_any_peer_connected(&nodes[a]) {
+               connect_dummy_node(&nodes[a]);
+               dummy_connected = true
+       }
+
        let events_1 = nodes[a].node.get_and_clear_pending_msg_events();
        assert_eq!(events_1.len(), 2);
-       let as_update = match events_1[0] {
+       let as_update = match events_1[1] {
                MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
                        msg.clone()
                },
                _ => panic!("Unexpected event"),
        };
-       match events_1[1] {
+       match events_1[0] {
                MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
                        assert_eq!(node_id, nodes[b].node.get_our_node_id());
                        assert_eq!(msg.data, expected_error);
@@ -3346,17 +3447,24 @@ pub fn handle_announce_close_broadcast_events<'a, 'b, 'c>(nodes: &Vec<Node<'a, '
                },
                _ => panic!("Unexpected event"),
        }
-
+       if dummy_connected {
+               disconnect_dummy_node(&nodes[a]);
+               dummy_connected = false;
+       }
+       if !is_any_peer_connected(&nodes[b]) {
+               connect_dummy_node(&nodes[b]);
+               dummy_connected = true;
+       }
        let events_2 = nodes[b].node.get_and_clear_pending_msg_events();
        assert_eq!(events_2.len(), if needs_err_handle { 1 } else { 2 });
-       let bs_update = match events_2[0] {
+       let bs_update = match events_2.last().unwrap() {
                MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
                        msg.clone()
                },
                _ => panic!("Unexpected event"),
        };
        if !needs_err_handle {
-               match events_2[1] {
+               match events_2[0] {
                        MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
                                assert_eq!(node_id, nodes[a].node.get_our_node_id());
                                assert_eq!(msg.data, expected_error);
@@ -3368,7 +3476,9 @@ pub fn handle_announce_close_broadcast_events<'a, 'b, 'c>(nodes: &Vec<Node<'a, '
                        _ => panic!("Unexpected event"),
                }
        }
-
+       if dummy_connected {
+               disconnect_dummy_node(&nodes[b]);
+       }
        for node in nodes {
                node.gossip_sync.handle_channel_update(&as_update).unwrap();
                node.gossip_sync.handle_channel_update(&bs_update).unwrap();
@@ -3753,7 +3863,7 @@ pub fn create_batch_channel_funding<'a, 'b, 'c>(
                                assert_eq!(channel_value_satoshis, event_channel_value_satoshis);
                                assert_eq!(user_channel_id, event_user_channel_id);
                                tx_outs.push(TxOut {
-                                       value: *channel_value_satoshis, script_pubkey: output_script.clone(),
+                                       value: Amount::from_sat(*channel_value_satoshis), script_pubkey: output_script.clone(),
                                });
                        },
                        _ => panic!("Unexpected event"),
@@ -3763,7 +3873,7 @@ pub fn create_batch_channel_funding<'a, 'b, 'c>(
 
        // Compose the batch funding transaction and give it to the ChannelManager.
        let tx = Transaction {
-               version: 2,
+               version: transaction::Version::TWO,
                lock_time: LockTime::ZERO,
                input: Vec::new(),
                output: tx_outs,
index c5ea582b27f81a95abd50bc760e26b8f30e363e5..df9ce5b3985b06d81c657fb7f7c52d707282f85f 100644 (file)
@@ -17,9 +17,9 @@ use crate::chain::chaininterface::LowerBoundedFeeEstimator;
 use crate::chain::channelmonitor;
 use crate::chain::channelmonitor::{CLOSED_CHANNEL_UPDATE_ID, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
 use crate::chain::transaction::OutPoint;
-use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, SignerProvider};
+use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, OutputSpender, SignerProvider};
 use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination, PaymentFailureReason};
-use crate::ln::{ChannelId, PaymentPreimage, PaymentSecret, PaymentHash};
+use crate::ln::types::{ChannelId, 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, get_holder_selected_channel_reserve_satoshis, OutboundV1Channel, InboundV1Channel, COINBASE_MATURITY, ChannelPhase};
 use crate::ln::channelmanager::{self, PaymentId, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, BREAKDOWN_TIMEOUT, ENABLE_GOSSIP_TICKS, DISABLE_GOSSIP_TICKS, MIN_CLTV_EXPIRY_DELTA};
 use crate::ln::channel::{DISCONNECT_PEER_AWAITING_RESPONSE_TICKS, ChannelError};
@@ -42,19 +42,17 @@ use bitcoin::blockdata::locktime::absolute::LockTime;
 use bitcoin::blockdata::script::{Builder, ScriptBuf};
 use bitcoin::blockdata::opcodes;
 use bitcoin::blockdata::constants::ChainHash;
-use bitcoin::network::constants::Network;
-use bitcoin::{Sequence, Transaction, TxIn, TxOut, Witness};
+use bitcoin::network::Network;
+use bitcoin::{Amount, Sequence, Transaction, TxIn, TxOut, Witness};
 use bitcoin::OutPoint as BitcoinOutPoint;
+use bitcoin::transaction::Version;
 
 use bitcoin::secp256k1::Secp256k1;
 use bitcoin::secp256k1::{PublicKey,SecretKey};
 
-use regex;
-
 use crate::io;
 use crate::prelude::*;
 use alloc::collections::BTreeSet;
-use core::default::Default;
 use core::iter::repeat;
 use bitcoin::hashes::Hash;
 use crate::sync::{Arc, Mutex, RwLock};
@@ -64,6 +62,38 @@ use crate::ln::chan_utils::CommitmentTransaction;
 
 use super::channel::UNFUNDED_CHANNEL_AGE_LIMIT_TICKS;
 
+#[test]
+fn test_channel_resumption_fail_post_funding() {
+       // If we fail to exchange funding with a peer prior to it disconnecting we'll resume the
+       // channel open on reconnect, however if we do exchange funding we do not currently support
+       // replaying it and here test that the channel closes.
+       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);
+
+       nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 1_000_000, 0, 42, None, None).unwrap();
+       let open_chan = 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_chan);
+       let accept_chan = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
+       nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &accept_chan);
+
+       let (temp_chan_id, tx, funding_output) =
+               create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42);
+       let new_chan_id = ChannelId::v1_from_funding_outpoint(funding_output);
+       nodes[0].node.funding_transaction_generated(&temp_chan_id, &nodes[1].node.get_our_node_id(), tx).unwrap();
+
+       nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
+       check_closed_events(&nodes[0], &[ExpectedCloseEvent::from_id_reason(new_chan_id, true, ClosureReason::DisconnectedPeer)]);
+
+       // After ddf75afd16 we'd panic on reconnection if we exchanged funding info, so test that
+       // explicitly here.
+       nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init {
+               features: nodes[1].node.init_features(), networks: None, remote_network_address: None
+       }, true).unwrap();
+       assert_eq!(nodes[0].node.get_and_clear_pending_msg_events(), Vec::new());
+}
+
 #[test]
 fn test_insane_channel_opens() {
        // Stand up a network of 2 nodes
@@ -682,7 +712,7 @@ fn test_update_fee_that_funder_cannot_afford() {
                //We made sure neither party's funds are below the dust limit and there are no HTLCs here
                assert_eq!(commitment_tx.output.len(), 2);
                let total_fee: u64 = commit_tx_fee_msat(feerate, 0, &channel_type_features) / 1000;
-               let mut actual_fee = commitment_tx.output.iter().fold(0, |acc, output| acc + output.value);
+               let mut actual_fee = commitment_tx.output.iter().fold(0, |acc, output| acc + output.value.to_sat());
                actual_fee = channel_value - actual_fee;
                assert_eq!(total_fee, actual_fee);
        }
@@ -1296,9 +1326,9 @@ fn test_duplicate_htlc_different_direction_onchain() {
        assert_eq!(remote_txn[0].output.len(), 4); // 1 local, 1 remote, 1 htlc inbound, 1 htlc outbound
        let mut has_both_htlcs = 0; // check htlcs match ones committed
        for outp in remote_txn[0].output.iter() {
-               if outp.value == 800_000 / 1000 {
+               if outp.value.to_sat() == 800_000 / 1000 {
                        has_both_htlcs += 1;
-               } else if outp.value == 900_000 / 1000 {
+               } else if outp.value.to_sat() == 900_000 / 1000 {
                        has_both_htlcs += 1;
                }
        }
@@ -1327,12 +1357,12 @@ fn test_duplicate_htlc_different_direction_onchain() {
 
        assert_eq!(preimage_tx.input.len(), 1);
        assert_eq!(preimage_tx.input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT); // HTLC 1 <--> 0, preimage tx
-       assert_eq!(remote_txn[0].output[preimage_tx.input[0].previous_output.vout as usize].value, 800);
+       assert_eq!(remote_txn[0].output[preimage_tx.input[0].previous_output.vout as usize].value.to_sat(), 800);
 
        assert_eq!(timeout_tx.input.len(), 1);
        assert_eq!(timeout_tx.input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT); // HTLC 0 <--> 1, timeout tx
        check_spends!(timeout_tx, remote_txn[0]);
-       assert_eq!(remote_txn[0].output[timeout_tx.input[0].previous_output.vout as usize].value, 900);
+       assert_eq!(remote_txn[0].output[timeout_tx.input[0].previous_output.vout as usize].value.to_sat(), 900);
 
        let events = nodes[0].node.get_and_clear_pending_msg_events();
        assert_eq!(events.len(), 3);
@@ -1404,8 +1434,9 @@ fn test_fee_spike_violation_fails_htlc() {
        let cur_height = nodes[1].node.best_block.read().unwrap().height + 1;
 
        let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
+       let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret);
        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();
+               3460001, &recipient_onion_fields, cur_height, &None).unwrap();
        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,
@@ -1601,8 +1632,9 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
        let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
        let cur_height = nodes[1].node.best_block.read().unwrap().height + 1;
        let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
+       let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret);
        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();
+               700_000, &recipient_onion_fields, cur_height, &None).unwrap();
        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,
@@ -1780,8 +1812,9 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
        let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
        let cur_height = nodes[0].node.best_block.read().unwrap().height + 1;
        let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route_2.paths[0], &session_priv).unwrap();
+       let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
        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();
+               &route_2.paths[0], recv_value_2, &recipient_onion_fields, cur_height, &None).unwrap();
        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,
@@ -2042,11 +2075,11 @@ fn test_channel_reserve_holding_cell_htlcs() {
                        assert_eq!(nodes[2].node.get_our_node_id(), receiver_node_id.unwrap());
                        assert_eq!(via_channel_id, Some(chan_2.2));
                        match &purpose {
-                               PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
+                               PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(our_payment_secret_21, *payment_secret);
                                },
-                               _ => panic!("expected PaymentPurpose::InvoicePayment")
+                               _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@ -2058,11 +2091,11 @@ fn test_channel_reserve_holding_cell_htlcs() {
                        assert_eq!(nodes[2].node.get_our_node_id(), receiver_node_id.unwrap());
                        assert_eq!(via_channel_id, Some(chan_2.2));
                        match &purpose {
-                               PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
+                               PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(our_payment_secret_22, *payment_secret);
                                },
-                               _ => panic!("expected PaymentPurpose::InvoicePayment")
+                               _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@ -2371,13 +2404,13 @@ fn channel_monitor_network_test() {
                connect_blocks(&nodes[3], TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS + 1);
                let events = nodes[3].node.get_and_clear_pending_msg_events();
                assert_eq!(events.len(), 2);
-               let close_chan_update_1 = match events[0] {
+               let close_chan_update_1 = match events[1] {
                        MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
                                msg.clone()
                        },
                        _ => panic!("Unexpected event"),
                };
-               match events[1] {
+               match events[0] {
                        MessageSendEvent::HandleError { action: ErrorAction::DisconnectPeer { .. }, node_id } => {
                                assert_eq!(node_id, nodes[4].node.get_our_node_id());
                        },
@@ -2403,13 +2436,13 @@ fn channel_monitor_network_test() {
                connect_blocks(&nodes[4], TEST_FINAL_CLTV - CLTV_CLAIM_BUFFER + 2);
                let events = nodes[4].node.get_and_clear_pending_msg_events();
                assert_eq!(events.len(), 2);
-               let close_chan_update_2 = match events[0] {
+               let close_chan_update_2 = match events[1] {
                        MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
                                msg.clone()
                        },
                        _ => panic!("Unexpected event"),
                };
-               match events[1] {
+               match events[0] {
                        MessageSendEvent::HandleError { action: ErrorAction::DisconnectPeer { .. }, node_id } => {
                                assert_eq!(node_id, nodes[3].node.get_our_node_id());
                        },
@@ -2417,7 +2450,7 @@ fn channel_monitor_network_test() {
                }
                check_added_monitors!(nodes[4], 1);
                test_txn_broadcast(&nodes[4], &chan_4, None, HTLCType::SUCCESS);
-               check_closed_event!(nodes[4], 1, ClosureReason::HolderForceClosed, [nodes[3].node.get_our_node_id()], 100000);
+               check_closed_event!(nodes[4], 1, ClosureReason::HTLCsTimedOut, [nodes[3].node.get_our_node_id()], 100000);
 
                mine_transaction(&nodes[4], &node_txn[0]);
                check_preimage_claim(&nodes[4], &node_txn);
@@ -2430,17 +2463,17 @@ fn channel_monitor_network_test() {
 
        assert_eq!(nodes[3].chain_monitor.chain_monitor.watch_channel(OutPoint { txid: chan_3.3.txid(), index: 0 }, chan_3_mon),
                Ok(ChannelMonitorUpdateStatus::Completed));
-       check_closed_event!(nodes[3], 1, ClosureReason::HolderForceClosed, [nodes[4].node.get_our_node_id()], 100000);
+       check_closed_event!(nodes[3], 1, ClosureReason::HTLCsTimedOut, [nodes[4].node.get_our_node_id()], 100000);
 }
 
 #[test]
 fn test_justice_tx_htlc_timeout() {
        // Test justice txn built on revoked HTLC-Timeout tx, against both sides
-       let mut alice_config = UserConfig::default();
+       let mut alice_config = test_default_channel_config();
        alice_config.channel_handshake_config.announced_channel = true;
        alice_config.channel_handshake_limits.force_announced_channel_preference = false;
        alice_config.channel_handshake_config.our_to_self_delay = 6 * 24 * 5;
-       let mut bob_config = UserConfig::default();
+       let mut bob_config = test_default_channel_config();
        bob_config.channel_handshake_config.announced_channel = true;
        bob_config.channel_handshake_limits.force_announced_channel_preference = false;
        bob_config.channel_handshake_config.our_to_self_delay = 6 * 24 * 3;
@@ -2499,11 +2532,11 @@ fn test_justice_tx_htlc_timeout() {
 #[test]
 fn test_justice_tx_htlc_success() {
        // Test justice txn built on revoked HTLC-Success tx, against both sides
-       let mut alice_config = UserConfig::default();
+       let mut alice_config = test_default_channel_config();
        alice_config.channel_handshake_config.announced_channel = true;
        alice_config.channel_handshake_limits.force_announced_channel_preference = false;
        alice_config.channel_handshake_config.our_to_self_delay = 6 * 24 * 5;
-       let mut bob_config = UserConfig::default();
+       let mut bob_config = test_default_channel_config();
        bob_config.channel_handshake_config.announced_channel = true;
        bob_config.channel_handshake_limits.force_announced_channel_preference = false;
        bob_config.channel_handshake_config.our_to_self_delay = 6 * 24 * 3;
@@ -2646,8 +2679,8 @@ fn do_test_forming_justice_tx_from_monitor_updates(broadcast_initial_commitment:
                }
        });
        // On the first commitment, node[1]'s balance was below dust so it didn't have an output
-       let node1_channel_balance = if broadcast_initial_commitment { 0 } else { revoked_commitment_tx.output[0].value };
-       let expected_claimable_balance = node1_channel_balance + justice_tx.output[0].value;
+       let node1_channel_balance = if broadcast_initial_commitment { 0 } else { revoked_commitment_tx.output[0].value.to_sat() };
+       let expected_claimable_balance = node1_channel_balance + justice_tx.output[0].value.to_sat();
        assert_eq!(total_claimable_balance, expected_claimable_balance);
 }
 
@@ -2750,7 +2783,7 @@ fn claim_htlc_outputs_single_tx() {
                check_added_monitors!(nodes[1], 1);
                check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed, [nodes[0].node.get_our_node_id()], 100000);
                let mut events = nodes[0].node.get_and_clear_pending_events();
-               expect_pending_htlcs_forwardable_from_events!(nodes[0], events[0..1], true);
+               expect_pending_htlcs_forwardable_conditions(events[0..2].to_vec(), &[HTLCDestination::FailedPayment { payment_hash: payment_hash_2 }]);
                match events.last().unwrap() {
                        Event::ChannelClosed { reason: ClosureReason::CommitmentTxConfirmed, .. } => {}
                        _ => panic!("Unexpected event"),
@@ -2867,8 +2900,8 @@ fn test_htlc_on_chain_success() {
        check_spends!(node_txn[1], commitment_tx[0]);
        assert_eq!(node_txn[0].input[0].witness.clone().last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
        assert_eq!(node_txn[1].input[0].witness.clone().last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
-       assert!(node_txn[0].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
-       assert!(node_txn[1].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
+       assert!(node_txn[0].output[0].script_pubkey.is_p2wsh()); // revokeable output
+       assert!(node_txn[1].output[0].script_pubkey.is_p2wsh()); // revokeable output
        assert_eq!(node_txn[0].lock_time, LockTime::ZERO);
        assert_eq!(node_txn[1].lock_time, LockTime::ZERO);
 
@@ -2960,13 +2993,13 @@ fn test_htlc_on_chain_success() {
                        if $htlc_offered {
                                assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
                                assert_eq!(node_txn[1].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
-                               assert!(node_txn[0].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
-                               assert!(node_txn[1].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
+                               assert!(node_txn[0].output[0].script_pubkey.is_p2wsh()); // revokeable output
+                               assert!(node_txn[1].output[0].script_pubkey.is_p2wsh()); // revokeable output
                        } else {
                                assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
                                assert_eq!(node_txn[1].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
-                               assert!(node_txn[0].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
-                               assert!(node_txn[1].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
+                               assert!(node_txn[0].output[0].script_pubkey.is_p2wpkh()); // direct payment
+                               assert!(node_txn[1].output[0].script_pubkey.is_p2wpkh()); // direct payment
                        }
                        node_txn.clear();
                } }
@@ -3008,7 +3041,7 @@ fn test_htlc_on_chain_success() {
        assert_eq!(commitment_spend.input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
        assert_eq!(commitment_spend.input[1].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
        assert_eq!(commitment_spend.lock_time.to_consensus_u32(), nodes[1].best_block_info().1);
-       assert!(commitment_spend.output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
+       assert!(commitment_spend.output[0].script_pubkey.is_p2wpkh()); // direct payment
        // We don't bother to check that B can claim the HTLC output on its commitment tx here as
        // we already checked the same situation with A.
 
@@ -3312,13 +3345,13 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
                let events = nodes[1].node.get_and_clear_pending_events();
                assert_eq!(events.len(), 2);
                match events[0] {
-                       Event::PendingHTLCsForwardable { .. } => { },
-                       _ => panic!("Unexpected event"),
-               };
-               match events[1] {
                        Event::HTLCHandlingFailed { .. } => { },
                        _ => panic!("Unexpected event"),
                }
+               match events[1] {
+                       Event::PendingHTLCsForwardable { .. } => { },
+                       _ => panic!("Unexpected event"),
+               };
                // Deliberately don't process the pending fail-back so they all fail back at once after
                // block connection just like the !deliver_bs_raa case
        }
@@ -3504,8 +3537,9 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
                let secp_ctx = Secp256k1::new();
                let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
                let current_height = nodes[1].node.best_block.read().unwrap().height + 1;
+               let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret);
                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();
+                       &route.paths[0], 50_000, &recipient_onion_fields, 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).unwrap();
 
@@ -3737,10 +3771,10 @@ fn test_peer_disconnected_before_funding_broadcasted() {
                nodes[0].node.timer_tick_occurred();
        }
 
-       // Ensure that the channel is closed with `ClosureReason::HolderForceClosed`
-       // when the peers are disconnected and do not reconnect before the funding
-       // transaction is broadcasted.
-       check_closed_event!(&nodes[0], 2, ClosureReason::HolderForceClosed, true
+       // Ensure that the channel is closed with `ClosureReason::DisconnectedPeer` and a
+       // `DiscardFunding` event when the peers are disconnected and do not reconnect before the
+       // funding transaction is broadcasted.
+       check_closed_event!(&nodes[0], 2, ClosureReason::DisconnectedPeer, true
                , [nodes[1].node.get_our_node_id()], 1000000);
        check_closed_event!(&nodes[1], 1, ClosureReason::DisconnectedPeer, false
                , [nodes[0].node.get_our_node_id()], 1000000);
@@ -3779,7 +3813,10 @@ fn test_simple_peer_disconnect() {
        nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
        nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
 
-       claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], true, payment_preimage_3);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage_3)
+                       .skip_last(true)
+       );
        fail_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], true, payment_hash_5);
 
        let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
@@ -3957,11 +3994,11 @@ fn do_test_drop_messages_peer_disconnect(messages_delivered: u8, simulate_broken
                        assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
                        assert_eq!(via_channel_id, Some(channel_id));
                        match &purpose {
-                               PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
+                               PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(payment_secret_1, *payment_secret);
                                },
-                               _ => panic!("expected PaymentPurpose::InvoicePayment")
+                               _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@ -4322,11 +4359,11 @@ fn test_drop_messages_peer_disconnect_dual_htlc() {
                Event::PaymentClaimable { ref payment_hash, ref purpose, .. } => {
                        assert_eq!(payment_hash_2, *payment_hash);
                        match &purpose {
-                               PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
+                               PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(payment_secret_2, *payment_secret);
                                },
-                               _ => panic!("expected PaymentPurpose::InvoicePayment")
+                               _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@ -4616,7 +4653,7 @@ fn test_static_spendable_outputs_preimage_tx() {
                MessageSendEvent::UpdateHTLCs { .. } => {},
                _ => panic!("Unexpected event"),
        }
-       match events[1] {
+       match events[2] {
                MessageSendEvent::BroadcastChannelUpdate { .. } => {},
                _ => panic!("Unexepected event"),
        }
@@ -4659,7 +4696,7 @@ fn test_static_spendable_outputs_timeout_tx() {
        mine_transaction(&nodes[1], &commitment_tx[0]);
        check_added_monitors!(nodes[1], 1);
        let events = nodes[1].node.get_and_clear_pending_msg_events();
-       match events[0] {
+       match events[1] {
                MessageSendEvent::BroadcastChannelUpdate { .. } => {},
                _ => panic!("Unexpected event"),
        }
@@ -4910,7 +4947,7 @@ fn test_onchain_to_onchain_claim() {
        assert_eq!(c_txn.len(), 1);
        check_spends!(c_txn[0], commitment_tx[0]);
        assert_eq!(c_txn[0].input[0].witness.clone().last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
-       assert!(c_txn[0].output[0].script_pubkey.is_v0_p2wsh()); // revokeable output
+       assert!(c_txn[0].output[0].script_pubkey.is_p2wsh()); // revokeable output
        assert_eq!(c_txn[0].lock_time, LockTime::ZERO); // 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
@@ -4971,7 +5008,7 @@ fn test_onchain_to_onchain_claim() {
        assert_eq!(b_txn.len(), 1);
        check_spends!(b_txn[0], commitment_tx[0]);
        assert_eq!(b_txn[0].input[0].witness.clone().last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
-       assert!(b_txn[0].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
+       assert!(b_txn[0].output[0].script_pubkey.is_p2wpkh()); // direct payment
        assert_eq!(b_txn[0].lock_time.to_consensus_u32(), nodes[1].best_block_info().1); // Success tx
 
        check_closed_broadcast!(nodes[1], true);
@@ -5058,9 +5095,9 @@ fn test_duplicate_payment_hash_one_failure_one_success() {
                // (with value 900 sats) will be claimed in the below `claim_funds` call.
                if node_txn.len() > 2 {
                        assert_eq!(node_txn[2].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
-                       htlc_timeout_tx = if node_txn[2].output[0].value < 900 { node_txn[2].clone() } else { node_txn[0].clone() };
+                       htlc_timeout_tx = if node_txn[2].output[0].value.to_sat() < 900 { node_txn[2].clone() } else { node_txn[0].clone() };
                } else {
-                       htlc_timeout_tx = if node_txn[0].output[0].value < 900 { node_txn[1].clone() } else { node_txn[0].clone() };
+                       htlc_timeout_tx = if node_txn[0].output[0].value.to_sat() < 900 { node_txn[1].clone() } else { node_txn[0].clone() };
                }
        }
 
@@ -5075,7 +5112,7 @@ fn test_duplicate_payment_hash_one_failure_one_success() {
                MessageSendEvent::UpdateHTLCs { .. } => {},
                _ => panic!("Unexpected event"),
        }
-       match events[1] {
+       match events[2] {
                MessageSendEvent::BroadcastChannelUpdate { .. } => {},
                _ => panic!("Unexepected event"),
        }
@@ -5153,7 +5190,7 @@ fn test_dynamic_spendable_outputs_local_htlc_success_tx() {
                MessageSendEvent::UpdateHTLCs { .. } => {},
                _ => panic!("Unexpected event"),
        }
-       match events[1] {
+       match events[2] {
                MessageSendEvent::BroadcastChannelUpdate { .. } => {},
                _ => panic!("Unexepected event"),
        }
@@ -5351,7 +5388,7 @@ fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, anno
        connect_blocks(&nodes[2], ANTI_REORG_DELAY - 1);
        check_closed_broadcast!(nodes[2], true);
        if deliver_last_raa {
-               expect_pending_htlcs_forwardable_from_events!(nodes[2], events[0..1], true);
+               expect_pending_htlcs_forwardable_from_events!(nodes[2], events[1..2], true);
 
                let expected_destinations: Vec<HTLCDestination> = repeat(HTLCDestination::NextHopChannel { node_id: Some(nodes[3].node.get_our_node_id()), channel_id: chan_2_3.2 }).take(3).collect();
                expect_htlc_handling_failed_destinations!(nodes[2].node.get_and_clear_pending_events(), expected_destinations);
@@ -5682,7 +5719,7 @@ fn do_htlc_claim_local_commitment_only(use_dust: bool) {
        test_txn_broadcast(&nodes[1], &chan, None, if use_dust { HTLCType::NONE } else { HTLCType::SUCCESS });
        check_closed_broadcast!(nodes[1], true);
        check_added_monitors!(nodes[1], 1);
-       check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed, [nodes[0].node.get_our_node_id()], 100000);
+       check_closed_event!(nodes[1], 1, ClosureReason::HTLCsTimedOut, [nodes[0].node.get_our_node_id()], 100000);
 }
 
 fn do_htlc_claim_current_remote_commitment_only(use_dust: bool) {
@@ -5713,7 +5750,7 @@ fn do_htlc_claim_current_remote_commitment_only(use_dust: bool) {
        test_txn_broadcast(&nodes[0], &chan, None, HTLCType::NONE);
        check_closed_broadcast!(nodes[0], true);
        check_added_monitors!(nodes[0], 1);
-       check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed, [nodes[1].node.get_our_node_id()], 100000);
+       check_closed_event!(nodes[0], 1, ClosureReason::HTLCsTimedOut, [nodes[1].node.get_our_node_id()], 100000);
 }
 
 fn do_htlc_claim_previous_remote_commitment_only(use_dust: bool, check_revoke_no_close: bool) {
@@ -5759,7 +5796,7 @@ fn do_htlc_claim_previous_remote_commitment_only(use_dust: bool, check_revoke_no
                test_txn_broadcast(&nodes[0], &chan, None, HTLCType::NONE);
                check_closed_broadcast!(nodes[0], true);
                check_added_monitors!(nodes[0], 1);
-               check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed, [nodes[1].node.get_our_node_id()], 100000);
+               check_closed_event!(nodes[0], 1, ClosureReason::HTLCsTimedOut, [nodes[1].node.get_our_node_id()], 100000);
        } else {
                expect_payment_failed!(nodes[0], our_payment_hash, true);
        }
@@ -6182,7 +6219,7 @@ fn test_fail_holding_cell_htlc_upon_free_multihop() {
        // nodes[1]'s ChannelManager will now signal that we have HTLC forwards to process.
        let process_htlc_forwards_event = nodes[1].node.get_and_clear_pending_events();
        assert_eq!(process_htlc_forwards_event.len(), 2);
-       match &process_htlc_forwards_event[0] {
+       match &process_htlc_forwards_event[1] {
                &Event::PendingHTLCsForwardable { .. } => {},
                _ => panic!("Unexpected event"),
        }
@@ -6490,8 +6527,9 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
        let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
        let cur_height = nodes[0].node.best_block.read().unwrap().height + 1;
        let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::signing_only(), &route.paths[0], &session_priv).unwrap();
+       let recipient_onion_fields = RecipientOnionFields::secret_only(our_payment_secret);
        let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(
-               &route.paths[0], send_amt, RecipientOnionFields::secret_only(our_payment_secret), cur_height, &None).unwrap();
+               &route.paths[0], send_amt, &recipient_onion_fields, cur_height, &None).unwrap();
        let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash).unwrap();
 
        let mut msg = msgs::UpdateAddHTLC {
@@ -7334,6 +7372,9 @@ fn test_announce_disable_channels() {
        let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
        let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
 
+       // Connect a dummy node for proper future events broadcasting
+       connect_dummy_node(&nodes[0]);
+
        create_announced_chan_between_nodes(&nodes, 0, 1);
        create_announced_chan_between_nodes(&nodes, 1, 0);
        create_announced_chan_between_nodes(&nodes, 0, 1);
@@ -7440,8 +7481,8 @@ fn test_bump_penalty_txn_on_revoked_commitment() {
 
        let mut penalty_sum = 0;
        for outp in revoked_txn[0].output.iter() {
-               if outp.script_pubkey.is_v0_p2wsh() {
-                       penalty_sum += outp.value;
+               if outp.script_pubkey.is_p2wsh() {
+                       penalty_sum += outp.value.to_sat();
                }
        }
 
@@ -7462,7 +7503,7 @@ fn test_bump_penalty_txn_on_revoked_commitment() {
                assert_eq!(node_txn[0].input.len(), 3); // Penalty txn claims to_local, offered_htlc and received_htlc outputs
                assert_eq!(node_txn[0].output.len(), 1);
                check_spends!(node_txn[0], revoked_txn[0]);
-               let fee_1 = penalty_sum - node_txn[0].output[0].value;
+               let fee_1 = penalty_sum - node_txn[0].output[0].value.to_sat();
                feerate_1 = fee_1 * 1000 / node_txn[0].weight().to_wu();
                penalty_1 = node_txn[0].txid();
                node_txn.clear();
@@ -7482,7 +7523,7 @@ fn test_bump_penalty_txn_on_revoked_commitment() {
                        penalty_2 = node_txn[0].txid();
                        // Verify new bumped tx is different from last claiming transaction, we don't want spurrious rebroadcast
                        assert_ne!(penalty_2, penalty_1);
-                       let fee_2 = penalty_sum - node_txn[0].output[0].value;
+                       let fee_2 = penalty_sum - node_txn[0].output[0].value.to_sat();
                        feerate_2 = fee_2 * 1000 / node_txn[0].weight().to_wu();
                        // Verify 25% bump heuristic
                        assert!(feerate_2 * 100 >= feerate_1 * 125);
@@ -7505,7 +7546,7 @@ fn test_bump_penalty_txn_on_revoked_commitment() {
                        penalty_3 = node_txn[0].txid();
                        // Verify new bumped tx is different from last claiming transaction, we don't want spurrious rebroadcast
                        assert_ne!(penalty_3, penalty_2);
-                       let fee_3 = penalty_sum - node_txn[0].output[0].value;
+                       let fee_3 = penalty_sum - node_txn[0].output[0].value.to_sat();
                        feerate_3 = fee_3 * 1000 / node_txn[0].weight().to_wu();
                        // Verify 25% bump heuristic
                        assert!(feerate_3 * 100 >= feerate_2 * 125);
@@ -7543,7 +7584,7 @@ fn test_bump_penalty_txn_on_revoked_htlcs() {
        let route_params = RouteParameters::from_payment_params_and_value(payment_params, 3_000_000);
        let route = get_route(&nodes[1].node.get_our_node_id(), &route_params, &nodes[1].network_graph.read_only(), None,
                nodes[0].logger, &scorer, &Default::default(), &random_seed_bytes).unwrap();
-       send_along_route(&nodes[1], route, &[&nodes[0]], 3_000_000);
+       let failed_payment_hash = send_along_route(&nodes[1], route, &[&nodes[0]], 3_000_000).1;
 
        let revoked_local_txn = get_local_commitment_txn!(nodes[1], chan.2);
        assert_eq!(revoked_local_txn[0].input.len(), 1);
@@ -7582,7 +7623,7 @@ fn test_bump_penalty_txn_on_revoked_htlcs() {
        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);
+       expect_pending_htlcs_forwardable_conditions(events[0..2].to_vec(), &[HTLCDestination::FailedPayment { payment_hash: failed_payment_hash }]);
        match events.last().unwrap() {
                Event::ChannelClosed { reason: ClosureReason::CommitmentTxConfirmed, .. } => {}
                _ => panic!("Unexpected event"),
@@ -7724,7 +7765,7 @@ fn test_bump_penalty_txn_on_remote_commitment() {
 
                preimage = node_txn[0].txid();
                let index = node_txn[0].input[0].previous_output.vout;
-               let fee = remote_txn[0].output[index as usize].value - node_txn[0].output[0].value;
+               let fee = remote_txn[0].output[index as usize].value.to_sat() - node_txn[0].output[0].value.to_sat();
                feerate_preimage = fee * 1000 / node_txn[0].weight().to_wu();
 
                let (preimage_bump_tx, timeout_tx) = if node_txn[2].input[0].previous_output == node_txn[0].input[0].previous_output {
@@ -7739,7 +7780,7 @@ fn test_bump_penalty_txn_on_remote_commitment() {
 
                timeout = timeout_tx.txid();
                let index = timeout_tx.input[0].previous_output.vout;
-               let fee = remote_txn[0].output[index as usize].value - timeout_tx.output[0].value;
+               let fee = remote_txn[0].output[index as usize].value.to_sat() - timeout_tx.output[0].value.to_sat();
                feerate_timeout = fee * 1000 / timeout_tx.weight().to_wu();
 
                node_txn.clear();
@@ -7758,13 +7799,13 @@ fn test_bump_penalty_txn_on_remote_commitment() {
                check_spends!(preimage_bump, remote_txn[0]);
 
                let index = preimage_bump.input[0].previous_output.vout;
-               let fee = remote_txn[0].output[index as usize].value - preimage_bump.output[0].value;
+               let fee = remote_txn[0].output[index as usize].value.to_sat() - preimage_bump.output[0].value.to_sat();
                let new_feerate = fee * 1000 / preimage_bump.weight().to_wu();
                assert!(new_feerate * 100 > feerate_timeout * 125);
                assert_ne!(timeout, preimage_bump.txid());
 
                let index = node_txn[0].input[0].previous_output.vout;
-               let fee = remote_txn[0].output[index as usize].value - node_txn[0].output[0].value;
+               let fee = remote_txn[0].output[index as usize].value.to_sat() - node_txn[0].output[0].value.to_sat();
                let new_feerate = fee * 1000 / node_txn[0].weight().to_wu();
                assert!(new_feerate * 100 > feerate_preimage * 125);
                assert_ne!(preimage, node_txn[0].txid());
@@ -8222,8 +8263,9 @@ fn test_onion_value_mpp_set_calculation() {
                        let height = nodes[0].best_block_info().1;
                        let session_priv = SecretKey::from_slice(&session_priv).unwrap();
                        let mut onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
+                       let recipient_onion_fields = RecipientOnionFields::secret_only(our_payment_secret);
                        let (mut onion_payloads, _, _) = onion_utils::build_onion_payloads(&route.paths[0], 100_000,
-                               RecipientOnionFields::secret_only(our_payment_secret), height + 1, &None).unwrap();
+                               &recipient_onion_fields, height + 1, &None).unwrap();
                        // Edit amt_to_forward to simulate the sender having set
                        // the final amount and the routing node taking less fee
                        if let msgs::OutboundOnionPayload::Receive {
@@ -8258,7 +8300,9 @@ fn test_onion_value_mpp_set_calculation() {
        let ev = remove_first_msg_event_to_node(&expected_paths[1][0].node.get_our_node_id(), &mut events);
        pass_along_path(&nodes[0], expected_paths[1], 101_000, our_payment_hash.clone(), Some(our_payment_secret), ev, true, None);
 
-       claim_payment_along_route(&nodes[0], expected_paths, false, our_payment_preimage);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], expected_paths, our_payment_preimage)
+       );
 }
 
 fn do_test_overshoot_mpp(msat_amounts: &[u64], total_msat: u64) {
@@ -8324,7 +8368,9 @@ fn do_test_overshoot_mpp(msat_amounts: &[u64], total_msat: u64) {
                pass_along_path(&nodes[src_idx], expected_path, amount_received, our_payment_hash.clone(), Some(our_payment_secret), ev, became_claimable_now, None);
        }
 
-       claim_payment_along_route(&nodes[src_idx], &expected_paths, false, our_payment_preimage);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[src_idx], &expected_paths, our_payment_preimage)
+       );
 }
 
 #[test]
@@ -8356,7 +8402,9 @@ fn test_simple_mpp() {
        route.paths[1].hops[0].short_channel_id = chan_2_id;
        route.paths[1].hops[1].short_channel_id = chan_4_id;
        send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], 200_000, payment_hash, payment_secret);
-       claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], payment_preimage)
+       );
 }
 
 #[test]
@@ -8388,10 +8436,10 @@ fn test_preimage_storage() {
        match events[0] {
                Event::PaymentClaimable { ref purpose, .. } => {
                        match &purpose {
-                               PaymentPurpose::InvoicePayment { payment_preimage, .. } => {
+                               PaymentPurpose::Bolt11InvoicePayment { payment_preimage, .. } => {
                                        claim_payment(&nodes[0], &[&nodes[1]], payment_preimage.unwrap());
                                },
-                               _ => panic!("expected PaymentPurpose::InvoicePayment")
+                               _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@ -8654,7 +8702,7 @@ fn test_concurrent_monitor_claim() {
        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::HolderForceClosed, false,
+       check_closed_event!(&nodes[0], 1, ClosureReason::HTLCsTimedOut, false,
                [nodes[1].node.get_our_node_id()], 100000);
        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);
@@ -9387,7 +9435,7 @@ fn test_invalid_funding_tx() {
        let wit_program_script: ScriptBuf = wit_program.into();
        for output in tx.output.iter_mut() {
                // Make the confirmed funding transaction have a bogus script_pubkey
-               output.script_pubkey = ScriptBuf::new_v0_p2wsh(&wit_program_script.wscript_hash());
+               output.script_pubkey = ScriptBuf::new_p2wsh(&wit_program_script.wscript_hash());
        }
 
        nodes[0].node.funding_transaction_generated_unchecked(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone(), 0).unwrap();
@@ -9425,7 +9473,7 @@ fn test_invalid_funding_tx() {
        // long the ChannelMonitor will try to read 32 bytes from the second-to-last element, panicing
        // as its not 32 bytes long.
        let mut spend_tx = Transaction {
-               version: 2i32, lock_time: LockTime::ZERO,
+               version: Version::TWO, lock_time: LockTime::ZERO,
                input: tx.output.iter().enumerate().map(|(idx, _)| TxIn {
                        previous_output: BitcoinOutPoint {
                                txid: tx.txid(),
@@ -9436,7 +9484,7 @@ fn test_invalid_funding_tx() {
                        witness: Witness::from_slice(&channelmonitor::deliberately_bogus_accepted_htlc_witness())
                }).collect(),
                output: vec![TxOut {
-                       value: 1000,
+                       value: Amount::from_sat(1000),
                        script_pubkey: ScriptBuf::new(),
                }]
        };
@@ -9800,7 +9848,9 @@ fn test_inconsistent_mpp_params() {
        assert_eq!(events.len(), 1);
        pass_along_path(&nodes[0], &[&nodes[2], &nodes[3]], 15_000_000, our_payment_hash, Some(our_payment_secret), events.pop().unwrap(), true, None);
 
-       do_claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, our_payment_preimage);
+       do_claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], our_payment_preimage)
+       );
        expect_payment_sent(&nodes[0], our_payment_preimage, Some(None), true, true);
 }
 
@@ -9872,7 +9922,7 @@ enum ExposureEvent {
        AtUpdateFeeOutbound,
 }
 
-fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_event: ExposureEvent, on_holder_tx: bool, multiplier_dust_limit: bool) {
+fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_event: ExposureEvent, on_holder_tx: bool, multiplier_dust_limit: bool, apply_excess_fee: bool) {
        // Test that we properly reject dust HTLC violating our `max_dust_htlc_exposure_msat`
        // policy.
        //
@@ -9887,12 +9937,33 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
 
        let chanmon_cfgs = create_chanmon_cfgs(2);
        let mut config = test_default_channel_config();
+
+       // We hard-code the feerate values here but they're re-calculated furter down and asserted.
+       // If the values ever change below these constants should simply be updated.
+       const AT_FEE_OUTBOUND_HTLCS: u64 = 20;
+       let nondust_htlc_count_in_limit =
+       if exposure_breach_event == ExposureEvent::AtUpdateFeeOutbound  {
+               AT_FEE_OUTBOUND_HTLCS
+       } else { 0 };
+       let initial_feerate = if apply_excess_fee { 253 * 2 } else { 253 };
+       let expected_dust_buffer_feerate = initial_feerate + 2530;
+       let mut commitment_tx_cost = commit_tx_fee_msat(initial_feerate - 253, nondust_htlc_count_in_limit, &ChannelTypeFeatures::empty());
+       commitment_tx_cost +=
+               if on_holder_tx {
+                       htlc_success_tx_weight(&ChannelTypeFeatures::empty())
+               } else {
+                       htlc_timeout_tx_weight(&ChannelTypeFeatures::empty())
+               } * (initial_feerate as u64 - 253) / 1000 * nondust_htlc_count_in_limit;
+       {
+               let mut feerate_lock = chanmon_cfgs[0].fee_estimator.sat_per_kw.lock().unwrap();
+               *feerate_lock = initial_feerate;
+       }
        config.channel_config.max_dust_htlc_exposure = if multiplier_dust_limit {
                // Default test fee estimator rate is 253 sat/kw, so we set the multiplier to 5_000_000 / 253
                // to get roughly the same initial value as the default setting when this test was
                // originally written.
-               MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253)
-       } else { MaxDustHTLCExposure::FixedLimitMsat(5_000_000) }; // initial default setting value
+               MaxDustHTLCExposure::FeeRateMultiplier((5_000_000 + commitment_tx_cost) / 253)
+       } else { MaxDustHTLCExposure::FixedLimitMsat(5_000_000 + commitment_tx_cost) };
        let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
        let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config), None]);
        let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
@@ -9936,6 +10007,11 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
        let (announcement, as_update, bs_update) = create_chan_between_nodes_with_value_b(&nodes[0], &nodes[1], &channel_ready);
        update_nodes_with_chan_announce(&nodes, 0, 1, &announcement, &as_update, &bs_update);
 
+       {
+               let mut feerate_lock = chanmon_cfgs[0].fee_estimator.sat_per_kw.lock().unwrap();
+               *feerate_lock = 253;
+       }
+
        // Fetch a route in advance as we will be unable to once we're unable to send.
        let (mut route, payment_hash, _, payment_secret) =
                get_route_and_payment_hash!(nodes[0], nodes[1], 1000);
@@ -9945,16 +10021,25 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
                let chan_lock = per_peer_state.get(&nodes[1].node.get_our_node_id()).unwrap().lock().unwrap();
                let chan = chan_lock.channel_by_id.get(&channel_id).unwrap();
                (chan.context().get_dust_buffer_feerate(None) as u64,
-               chan.context().get_max_dust_htlc_exposure_msat(&LowerBoundedFeeEstimator(nodes[0].fee_estimator)))
+               chan.context().get_max_dust_htlc_exposure_msat(253))
        };
+       assert_eq!(dust_buffer_feerate, expected_dust_buffer_feerate as u64);
        let dust_outbound_htlc_on_holder_tx_msat: u64 = (dust_buffer_feerate * htlc_timeout_tx_weight(&channel_type_features) / 1000 + open_channel.common_fields.dust_limit_satoshis - 1) * 1000;
        let dust_outbound_htlc_on_holder_tx: u64 = max_dust_htlc_exposure_msat / dust_outbound_htlc_on_holder_tx_msat;
 
-       let dust_inbound_htlc_on_holder_tx_msat: u64 = (dust_buffer_feerate * htlc_success_tx_weight(&channel_type_features) / 1000 + open_channel.common_fields.dust_limit_satoshis - 1) * 1000;
+       // Substract 3 sats for multiplier and 2 sats for fixed limit to make sure we are 50% below the dust limit.
+       // This is to make sure we fully use the dust limit. If we don't, we could end up with `dust_ibd_htlc_on_holder_tx` being 1
+       // while `max_dust_htlc_exposure_msat` is not equal to `dust_outbound_htlc_on_holder_tx_msat`.
+       let dust_inbound_htlc_on_holder_tx_msat: u64 = (dust_buffer_feerate * htlc_success_tx_weight(&channel_type_features) / 1000 + open_channel.common_fields.dust_limit_satoshis - if multiplier_dust_limit { 3 } else { 2 }) * 1000;
        let dust_inbound_htlc_on_holder_tx: u64 = max_dust_htlc_exposure_msat / dust_inbound_htlc_on_holder_tx_msat;
 
+       // This test was written with a fixed dust value here, which we retain, but assert that it is,
+       // indeed, dust on both transactions.
        let dust_htlc_on_counterparty_tx: u64 = 4;
-       let dust_htlc_on_counterparty_tx_msat: u64 = max_dust_htlc_exposure_msat / dust_htlc_on_counterparty_tx;
+       let dust_htlc_on_counterparty_tx_msat: u64 = 1_250_000;
+       let calcd_dust_htlc_on_counterparty_tx_msat: u64 = (dust_buffer_feerate * htlc_timeout_tx_weight(&channel_type_features) / 1000 + open_channel.common_fields.dust_limit_satoshis - if multiplier_dust_limit { 3 } else { 2 }) * 1000;
+       assert!(dust_htlc_on_counterparty_tx_msat < dust_inbound_htlc_on_holder_tx_msat);
+       assert!(dust_htlc_on_counterparty_tx_msat < calcd_dust_htlc_on_counterparty_tx_msat);
 
        if on_holder_tx {
                if dust_outbound_balance {
@@ -10024,7 +10109,7 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
                        // Outbound dust balance: 5200 sats
                        nodes[0].logger.assert_log("lightning::ln::channel",
                                format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx",
-                                       dust_htlc_on_counterparty_tx_msat * (dust_htlc_on_counterparty_tx - 1) + dust_htlc_on_counterparty_tx_msat + 4,
+                                       dust_htlc_on_counterparty_tx_msat * dust_htlc_on_counterparty_tx + commitment_tx_cost + 4,
                                        max_dust_htlc_exposure_msat), 1);
                }
        } else if exposure_breach_event == ExposureEvent::AtUpdateFeeOutbound {
@@ -10032,7 +10117,7 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
                // For the multiplier dust exposure limit, since it scales with feerate,
                // we need to add a lot of HTLCs that will become dust at the new feerate
                // to cross the threshold.
-               for _ in 0..20 {
+               for _ in 0..AT_FEE_OUTBOUND_HTLCS {
                        let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(1_000), None);
                        nodes[0].node.send_payment_with_route(&route, payment_hash,
                                RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
@@ -10051,27 +10136,123 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
        added_monitors.clear();
 }
 
-fn do_test_max_dust_htlc_exposure_by_threshold_type(multiplier_dust_limit: bool) {
-       do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, true, multiplier_dust_limit);
-       do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, true, multiplier_dust_limit);
-       do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, true, multiplier_dust_limit);
-       do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, false, multiplier_dust_limit);
-       do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, false, multiplier_dust_limit);
-       do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, false, multiplier_dust_limit);
-       do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, true, multiplier_dust_limit);
-       do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, false, multiplier_dust_limit);
-       do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, true, multiplier_dust_limit);
-       do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, false, multiplier_dust_limit);
-       do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, false, multiplier_dust_limit);
-       do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, true, multiplier_dust_limit);
+fn do_test_max_dust_htlc_exposure_by_threshold_type(multiplier_dust_limit: bool, apply_excess_fee: bool) {
+       do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, true, multiplier_dust_limit, apply_excess_fee);
+       do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, true, multiplier_dust_limit, apply_excess_fee);
+       do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, true, multiplier_dust_limit, apply_excess_fee);
+       do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCReception, false, multiplier_dust_limit, apply_excess_fee);
+       do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCForward, false, multiplier_dust_limit, apply_excess_fee);
+       do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, false, multiplier_dust_limit, apply_excess_fee);
+       do_test_max_dust_htlc_exposure(true, ExposureEvent::AtHTLCReception, true, multiplier_dust_limit, apply_excess_fee);
+       do_test_max_dust_htlc_exposure(false, ExposureEvent::AtHTLCForward, false, multiplier_dust_limit, apply_excess_fee);
+       if !multiplier_dust_limit && !apply_excess_fee {
+               // Because non-dust HTLC transaction fees are included in the dust exposure, trying to
+               // increase the fee to hit a higher dust exposure with a
+               // `MaxDustHTLCExposure::FeeRateMultiplier` is no longer super practical, so we skip these
+               // in the `multiplier_dust_limit` case.
+               do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, true, multiplier_dust_limit, apply_excess_fee);
+               do_test_max_dust_htlc_exposure(true, ExposureEvent::AtUpdateFeeOutbound, false, multiplier_dust_limit, apply_excess_fee);
+               do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, false, multiplier_dust_limit, apply_excess_fee);
+               do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, true, multiplier_dust_limit, apply_excess_fee);
+       }
 }
 
 #[test]
 fn test_max_dust_htlc_exposure() {
-       do_test_max_dust_htlc_exposure_by_threshold_type(false);
-       do_test_max_dust_htlc_exposure_by_threshold_type(true);
+       do_test_max_dust_htlc_exposure_by_threshold_type(false, false);
+       do_test_max_dust_htlc_exposure_by_threshold_type(false, true);
+       do_test_max_dust_htlc_exposure_by_threshold_type(true, false);
+       do_test_max_dust_htlc_exposure_by_threshold_type(true, true);
+}
+
+#[test]
+fn test_nondust_htlc_fees_are_dust() {
+       // Test that the transaction fees paid in nondust HTLCs count towards our dust limit
+       let chanmon_cfgs = create_chanmon_cfgs(3);
+       let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+
+       let mut config = test_default_channel_config();
+       // Set the dust limit to the default value
+       config.channel_config.max_dust_htlc_exposure =
+               MaxDustHTLCExposure::FeeRateMultiplier(10_000);
+       // Make sure the HTLC limits don't get in the way
+       config.channel_handshake_limits.min_max_accepted_htlcs = 400;
+       config.channel_handshake_config.our_max_accepted_htlcs = 400;
+       config.channel_handshake_config.our_htlc_minimum_msat = 1;
+
+       let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[Some(config), Some(config), Some(config)]);
+       let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+       // Create a channel from 1 -> 0 but immediately push all of the funds towards 0
+       let chan_id_1 = create_announced_chan_between_nodes(&nodes, 1, 0).2;
+       while nodes[1].node.list_channels()[0].next_outbound_htlc_limit_msat > 0 {
+               send_payment(&nodes[1], &[&nodes[0]], nodes[1].node.list_channels()[0].next_outbound_htlc_limit_msat);
+       }
+
+       // First get the channel one HTLC_VALUE HTLC away from the dust limit by sending dust HTLCs
+       // repeatedly until we run out of space.
+       const HTLC_VALUE: u64 = 1_000_000; // Doesn't matter, tune until the test passes
+       let payment_preimage = route_payment(&nodes[0], &[&nodes[1]], HTLC_VALUE).0;
+
+       while nodes[0].node.list_channels()[0].next_outbound_htlc_minimum_msat == 0 {
+               route_payment(&nodes[0], &[&nodes[1]], HTLC_VALUE);
+       }
+       assert_ne!(nodes[0].node.list_channels()[0].next_outbound_htlc_limit_msat, 0,
+               "We don't want to run out of ability to send because of some non-dust limit");
+       assert!(nodes[0].node.list_channels()[0].pending_outbound_htlcs.len() < 10,
+               "We should be able to fill our dust limit without too many HTLCs");
+
+       let dust_limit = nodes[0].node.list_channels()[0].next_outbound_htlc_minimum_msat;
+       claim_payment(&nodes[0], &[&nodes[1]], payment_preimage);
+       assert_ne!(nodes[0].node.list_channels()[0].next_outbound_htlc_minimum_msat, 0,
+               "Make sure we are able to send once we clear one HTLC");
+
+       // At this point we have somewhere between dust_limit and dust_limit * 2 left in our dust
+       // exposure limit, and we want to max that out using non-dust HTLCs.
+       let commitment_tx_per_htlc_cost =
+               htlc_success_tx_weight(&ChannelTypeFeatures::empty()) * 253;
+       let max_htlcs_remaining = dust_limit * 2 / commitment_tx_per_htlc_cost;
+       assert!(max_htlcs_remaining < 30,
+               "We should be able to fill our dust limit without too many HTLCs");
+       for i in 0..max_htlcs_remaining + 1 {
+               assert_ne!(i, max_htlcs_remaining);
+               if nodes[0].node.list_channels()[0].next_outbound_htlc_limit_msat < dust_limit {
+                       // We found our limit, and it was less than max_htlcs_remaining!
+                       // At this point we can only send dust HTLCs as any non-dust HTLCs will overuse our
+                       // remaining dust exposure.
+                       break;
+               }
+               route_payment(&nodes[0], &[&nodes[1]], dust_limit * 2);
+       }
+
+       // At this point non-dust HTLCs are no longer accepted from node 0 -> 1, we also check that
+       // such HTLCs can't be routed over the same channel either.
+       create_announced_chan_between_nodes(&nodes, 2, 0);
+       let (route, payment_hash, _, payment_secret) =
+               get_route_and_payment_hash!(nodes[2], nodes[1], dust_limit * 2);
+       let onion = RecipientOnionFields::secret_only(payment_secret);
+       nodes[2].node.send_payment_with_route(&route, payment_hash, onion, PaymentId([0; 32])).unwrap();
+       check_added_monitors(&nodes[2], 1);
+       let send = SendEvent::from_node(&nodes[2]);
+
+       nodes[0].node.handle_update_add_htlc(&nodes[2].node.get_our_node_id(), &send.msgs[0]);
+       commitment_signed_dance!(nodes[0], nodes[2], send.commitment_msg, false, true);
+
+       expect_pending_htlcs_forwardable!(nodes[0]);
+       check_added_monitors(&nodes[0], 1);
+       let node_id_1 = nodes[1].node.get_our_node_id();
+       expect_htlc_handling_failed_destinations!(
+               nodes[0].node.get_and_clear_pending_events(),
+               &[HTLCDestination::NextHopChannel { node_id: Some(node_id_1), channel_id: chan_id_1 }]
+       );
+
+       let fail = get_htlc_update_msgs(&nodes[0], &nodes[2].node.get_our_node_id());
+       nodes[2].node.handle_update_fail_htlc(&nodes[0].node.get_our_node_id(), &fail.update_fail_htlcs[0]);
+       commitment_signed_dance!(nodes[2], nodes[0], fail.commitment_signed, false);
+       expect_payment_failed_conditions(&nodes[2], payment_hash, false, PaymentFailedConditions::new());
 }
 
+
 #[test]
 fn test_non_final_funding_tx() {
        let chanmon_cfgs = create_chanmon_cfgs(2);
@@ -10094,8 +10275,8 @@ fn test_non_final_funding_tx() {
        let mut tx = match events[0] {
                Event::FundingGenerationReady { ref channel_value_satoshis, ref output_script, .. } => {
                        // Timelock the transaction _beyond_ the best client height + 1.
-                       Transaction { version: chan_id as i32, lock_time: LockTime::from_height(best_height + 2).unwrap(), input: vec![input], output: vec![TxOut {
-                               value: *channel_value_satoshis, script_pubkey: output_script.clone(),
+                       Transaction { version: Version(chan_id as i32), lock_time: LockTime::from_height(best_height + 2).unwrap(), input: vec![input], output: vec![TxOut {
+                               value: Amount::from_sat(*channel_value_satoshis), script_pubkey: output_script.clone(),
                        }]}
                },
                _ => panic!("Unexpected event"),
@@ -10107,14 +10288,9 @@ fn test_non_final_funding_tx() {
                },
                _ => panic!()
        }
-       let events = nodes[0].node.get_and_clear_pending_events();
-       assert_eq!(events.len(), 1);
-       match events[0] {
-               Event::ChannelClosed { channel_id, .. } => {
-                       assert_eq!(channel_id, temp_channel_id);
-               },
-               _ => panic!("Unexpected event"),
-       }
+       let err = "Error in transaction funding: Misuse error: Funding transaction absolute timelock is non-final".to_owned();
+       check_closed_events(&nodes[0], &[ExpectedCloseEvent::from_id_reason(temp_channel_id, false, ClosureReason::ProcessingError { err })]);
+       assert_eq!(get_err_msg(&nodes[0], &nodes[1].node.get_our_node_id()).data, "Failed to fund channel");
 }
 
 #[test]
@@ -10139,8 +10315,8 @@ fn test_non_final_funding_tx_within_headroom() {
        let mut tx = match events[0] {
                Event::FundingGenerationReady { ref channel_value_satoshis, ref output_script, .. } => {
                        // Timelock the transaction within a +1 headroom from the best block.
-                       Transaction { version: chan_id as i32, lock_time: LockTime::from_consensus(best_height + 1), input: vec![input], output: vec![TxOut {
-                               value: *channel_value_satoshis, script_pubkey: output_script.clone(),
+                       Transaction { version: Version(chan_id as i32), lock_time: LockTime::from_consensus(best_height + 1), input: vec![input], output: vec![TxOut {
+                               value: Amount::from_sat(*channel_value_satoshis), script_pubkey: output_script.clone(),
                        }]}
                },
                _ => panic!("Unexpected event"),
@@ -10983,3 +11159,36 @@ fn test_funding_and_commitment_tx_confirm_same_block() {
        do_test_funding_and_commitment_tx_confirm_same_block(false);
        do_test_funding_and_commitment_tx_confirm_same_block(true);
 }
+
+#[test]
+fn test_accept_inbound_channel_errors_queued() {
+       // For manually accepted inbound channels, tests that a close error is correctly handled
+       // and the channel fails for the initiator.
+       let mut config0 = test_default_channel_config();
+       let mut config1 = config0.clone();
+       config1.channel_handshake_limits.their_to_self_delay = 1000;
+       config1.manually_accept_inbound_channels = true;
+       config0.channel_handshake_config.our_to_self_delay = 2000;
+
+       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, &[Some(config0), Some(config1)]);
+       let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+       nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, 0, 42, None, None).unwrap();
+       let open_channel_msg = 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_msg);
+       let events = nodes[1].node.get_and_clear_pending_events();
+       match events[0] {
+               Event::OpenChannelRequest { temporary_channel_id, .. } => {
+                       match nodes[1].node.accept_inbound_channel(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 23) {
+                               Err(APIError::ChannelUnavailable { err: _ }) => (),
+                               _ => panic!(),
+                       }
+               }
+               _ => panic!("Unexpected event"),
+       }
+       assert_eq!(get_err_msg(&nodes[1], &nodes[0].node.get_our_node_id()).channel_id,
+               open_channel_msg.common_fields.temporary_channel_id);
+}
index eeae514fdf3ce4097f79487ed09ceace231cadd4..a85d93b7135e5c9b0e41d2750359c2f335654827 100644 (file)
@@ -9,13 +9,12 @@
 
 //! Utilities to generate inbound payment information in service of invoice creation.
 
-use alloc::string::ToString;
 use bitcoin::hashes::{Hash, HashEngine};
 use bitcoin::hashes::cmp::fixed_time_eq;
 use bitcoin::hashes::hmac::{Hmac, HmacEngine};
 use bitcoin::hashes::sha256::Hash as Sha256;
 use crate::sign::{KeyMaterial, EntropySource};
-use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
+use crate::ln::types::{PaymentHash, PaymentPreimage, PaymentSecret};
 use crate::ln::msgs;
 use crate::ln::msgs::MAX_VALUE_MSAT;
 use crate::crypto::chacha20::ChaCha20;
@@ -23,7 +22,9 @@ use crate::crypto::utils::hkdf_extract_expand_5x;
 use crate::util::errors::APIError;
 use crate::util::logger::Logger;
 
-use core::convert::{TryFrom, TryInto};
+#[allow(unused_imports)]
+use crate::prelude::*;
+
 use core::ops::Deref;
 
 pub(crate) const IV_LEN: usize = 16;
diff --git a/lightning/src/ln/interactivetxs.rs b/lightning/src/ln/interactivetxs.rs
new file mode 100644 (file)
index 0000000..17c6990
--- /dev/null
@@ -0,0 +1,1650 @@
+// 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.
+
+use crate::io_extras::sink;
+use crate::prelude::*;
+use core::ops::Deref;
+
+use bitcoin::amount::Amount;
+use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
+use bitcoin::consensus::Encodable;
+use bitcoin::policy::MAX_STANDARD_TX_WEIGHT;
+use bitcoin::transaction::Version;
+use bitcoin::{
+       absolute::LockTime as AbsoluteLockTime, OutPoint, ScriptBuf, Sequence, Transaction, TxIn,
+       TxOut, Weight,
+};
+
+use crate::chain::chaininterface::fee_for_weight;
+use crate::events::bump_transaction::{BASE_INPUT_WEIGHT, EMPTY_SCRIPT_SIG_WEIGHT};
+use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
+use crate::ln::msgs;
+use crate::ln::msgs::SerialId;
+use crate::ln::types::ChannelId;
+use crate::sign::{EntropySource, P2TR_KEY_PATH_WITNESS_WEIGHT, P2WPKH_WITNESS_WEIGHT};
+use crate::util::ser::TransactionU16LenLimited;
+
+/// The number of received `tx_add_input` messages during a negotiation at which point the
+/// negotiation MUST be failed.
+const MAX_RECEIVED_TX_ADD_INPUT_COUNT: u16 = 4096;
+
+/// The number of received `tx_add_output` messages during a negotiation at which point the
+/// negotiation MUST be failed.
+const MAX_RECEIVED_TX_ADD_OUTPUT_COUNT: u16 = 4096;
+
+/// The number of inputs or outputs that the state machine can have, before it MUST fail the
+/// negotiation.
+const MAX_INPUTS_OUTPUTS_COUNT: usize = 252;
+
+/// The total weight of the common fields whose fee is paid by the initiator of the interactive
+/// transaction construction protocol.
+const TX_COMMON_FIELDS_WEIGHT: u64 = (4 /* version */ + 4 /* locktime */ + 1 /* input count */ +
+       1 /* output count */) * WITNESS_SCALE_FACTOR as u64 + 2 /* segwit marker + flag */;
+
+// BOLT 3 - Lower bounds for input weights
+
+/// Lower bound for P2WPKH input weight
+pub(crate) const P2WPKH_INPUT_WEIGHT_LOWER_BOUND: u64 =
+       BASE_INPUT_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT + P2WPKH_WITNESS_WEIGHT;
+
+/// Lower bound for P2WSH input weight is chosen as same as P2WPKH input weight in BOLT 3
+pub(crate) const P2WSH_INPUT_WEIGHT_LOWER_BOUND: u64 = P2WPKH_INPUT_WEIGHT_LOWER_BOUND;
+
+/// Lower bound for P2TR input weight is chosen as the key spend path.
+/// Not specified in BOLT 3, but a reasonable lower bound.
+pub(crate) const P2TR_INPUT_WEIGHT_LOWER_BOUND: u64 =
+       BASE_INPUT_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT + P2TR_KEY_PATH_WITNESS_WEIGHT;
+
+/// Lower bound for unknown segwit version input weight is chosen the same as P2WPKH in BOLT 3
+pub(crate) const UNKNOWN_SEGWIT_VERSION_INPUT_WEIGHT_LOWER_BOUND: u64 =
+       P2WPKH_INPUT_WEIGHT_LOWER_BOUND;
+
+trait SerialIdExt {
+       fn is_for_initiator(&self) -> bool;
+       fn is_for_non_initiator(&self) -> bool;
+}
+
+impl SerialIdExt for SerialId {
+       fn is_for_initiator(&self) -> bool {
+               self % 2 == 0
+       }
+
+       fn is_for_non_initiator(&self) -> bool {
+               !self.is_for_initiator()
+       }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub(crate) enum AbortReason {
+       InvalidStateTransition,
+       UnexpectedCounterpartyMessage,
+       ReceivedTooManyTxAddInputs,
+       ReceivedTooManyTxAddOutputs,
+       IncorrectInputSequenceValue,
+       IncorrectSerialIdParity,
+       SerialIdUnknown,
+       DuplicateSerialId,
+       PrevTxOutInvalid,
+       ExceededMaximumSatsAllowed,
+       ExceededNumberOfInputsOrOutputs,
+       TransactionTooLarge,
+       BelowDustLimit,
+       InvalidOutputScript,
+       InsufficientFees,
+       OutputsValueExceedsInputsValue,
+       InvalidTx,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct InteractiveTxInput {
+       serial_id: SerialId,
+       input: TxIn,
+       prev_output: TxOut,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct InteractiveTxOutput {
+       serial_id: SerialId,
+       tx_out: TxOut,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct ConstructedTransaction {
+       holder_is_initiator: bool,
+
+       inputs: Vec<InteractiveTxInput>,
+       outputs: Vec<InteractiveTxOutput>,
+
+       local_inputs_value_satoshis: u64,
+       local_outputs_value_satoshis: u64,
+
+       remote_inputs_value_satoshis: u64,
+       remote_outputs_value_satoshis: u64,
+
+       lock_time: AbsoluteLockTime,
+}
+
+impl ConstructedTransaction {
+       fn new(context: NegotiationContext) -> Self {
+               let local_inputs_value_satoshis = context
+                       .inputs
+                       .iter()
+                       .filter(|(serial_id, _)| {
+                               !is_serial_id_valid_for_counterparty(context.holder_is_initiator, serial_id)
+                       })
+                       .fold(0u64, |value, (_, input)| value.saturating_add(input.prev_output.value.to_sat()));
+
+               let local_outputs_value_satoshis = context
+                       .outputs
+                       .iter()
+                       .filter(|(serial_id, _)| {
+                               !is_serial_id_valid_for_counterparty(context.holder_is_initiator, serial_id)
+                       })
+                       .fold(0u64, |value, (_, output)| value.saturating_add(output.tx_out.value.to_sat()));
+
+               Self {
+                       holder_is_initiator: context.holder_is_initiator,
+
+                       local_inputs_value_satoshis,
+                       local_outputs_value_satoshis,
+
+                       remote_inputs_value_satoshis: context.remote_inputs_value(),
+                       remote_outputs_value_satoshis: context.remote_outputs_value(),
+
+                       inputs: context.inputs.into_values().collect(),
+                       outputs: context.outputs.into_values().collect(),
+
+                       lock_time: context.tx_locktime,
+               }
+       }
+
+       pub fn weight(&self) -> Weight {
+               let inputs_weight = self.inputs.iter().fold(
+                       Weight::from_wu(0),
+                       |weight, InteractiveTxInput { prev_output, .. }| {
+                               weight.checked_add(estimate_input_weight(prev_output)).unwrap_or(Weight::MAX)
+                       },
+               );
+               let outputs_weight = self.outputs.iter().fold(
+                       Weight::from_wu(0),
+                       |weight, InteractiveTxOutput { tx_out, .. }| {
+                               weight.checked_add(get_output_weight(&tx_out.script_pubkey)).unwrap_or(Weight::MAX)
+                       },
+               );
+               Weight::from_wu(TX_COMMON_FIELDS_WEIGHT)
+                       .checked_add(inputs_weight)
+                       .and_then(|weight| weight.checked_add(outputs_weight))
+                       .unwrap_or(Weight::MAX)
+       }
+
+       pub fn into_unsigned_tx(self) -> Transaction {
+               // Inputs and outputs must be sorted by serial_id
+               let ConstructedTransaction { mut inputs, mut outputs, .. } = self;
+
+               inputs.sort_unstable_by_key(|InteractiveTxInput { serial_id, .. }| *serial_id);
+               outputs.sort_unstable_by_key(|InteractiveTxOutput { serial_id, .. }| *serial_id);
+
+               let input: Vec<TxIn> =
+                       inputs.into_iter().map(|InteractiveTxInput { input, .. }| input).collect();
+               let output: Vec<TxOut> =
+                       outputs.into_iter().map(|InteractiveTxOutput { tx_out, .. }| tx_out).collect();
+
+               Transaction { version: Version::TWO, lock_time: self.lock_time, input, output }
+       }
+}
+
+#[derive(Debug)]
+struct NegotiationContext {
+       holder_is_initiator: bool,
+       received_tx_add_input_count: u16,
+       received_tx_add_output_count: u16,
+       inputs: HashMap<SerialId, InteractiveTxInput>,
+       prevtx_outpoints: HashSet<OutPoint>,
+       outputs: HashMap<SerialId, InteractiveTxOutput>,
+       tx_locktime: AbsoluteLockTime,
+       feerate_sat_per_kw: u32,
+}
+
+pub(crate) fn estimate_input_weight(prev_output: &TxOut) -> Weight {
+       Weight::from_wu(if prev_output.script_pubkey.is_p2wpkh() {
+               P2WPKH_INPUT_WEIGHT_LOWER_BOUND
+       } else if prev_output.script_pubkey.is_p2wsh() {
+               P2WSH_INPUT_WEIGHT_LOWER_BOUND
+       } else if prev_output.script_pubkey.is_p2tr() {
+               P2TR_INPUT_WEIGHT_LOWER_BOUND
+       } else {
+               UNKNOWN_SEGWIT_VERSION_INPUT_WEIGHT_LOWER_BOUND
+       })
+}
+
+pub(crate) fn get_output_weight(script_pubkey: &ScriptBuf) -> Weight {
+       Weight::from_wu(
+               (8 /* value */ + script_pubkey.consensus_encode(&mut sink()).unwrap() as u64)
+                       * WITNESS_SCALE_FACTOR as u64,
+       )
+}
+
+fn is_serial_id_valid_for_counterparty(holder_is_initiator: bool, serial_id: &SerialId) -> bool {
+       // A received `SerialId`'s parity must match the role of the counterparty.
+       holder_is_initiator == serial_id.is_for_non_initiator()
+}
+
+impl NegotiationContext {
+       fn is_serial_id_valid_for_counterparty(&self, serial_id: &SerialId) -> bool {
+               is_serial_id_valid_for_counterparty(self.holder_is_initiator, serial_id)
+       }
+
+       fn remote_inputs_value(&self) -> u64 {
+               self.inputs
+                       .iter()
+                       .filter(|(serial_id, _)| self.is_serial_id_valid_for_counterparty(serial_id))
+                       .fold(0u64, |acc, (_, InteractiveTxInput { prev_output, .. })| {
+                               acc.saturating_add(prev_output.value.to_sat())
+                       })
+       }
+
+       fn remote_outputs_value(&self) -> u64 {
+               self.outputs
+                       .iter()
+                       .filter(|(serial_id, _)| self.is_serial_id_valid_for_counterparty(serial_id))
+                       .fold(0u64, |acc, (_, InteractiveTxOutput { tx_out, .. })| {
+                               acc.saturating_add(tx_out.value.to_sat())
+                       })
+       }
+
+       fn remote_inputs_weight(&self) -> Weight {
+               Weight::from_wu(
+                       self.inputs
+                               .iter()
+                               .filter(|(serial_id, _)| self.is_serial_id_valid_for_counterparty(serial_id))
+                               .fold(0u64, |weight, (_, InteractiveTxInput { prev_output, .. })| {
+                                       weight.saturating_add(estimate_input_weight(prev_output).to_wu())
+                               }),
+               )
+       }
+
+       fn remote_outputs_weight(&self) -> Weight {
+               Weight::from_wu(
+                       self.outputs
+                               .iter()
+                               .filter(|(serial_id, _)| self.is_serial_id_valid_for_counterparty(serial_id))
+                               .fold(0u64, |weight, (_, InteractiveTxOutput { tx_out, .. })| {
+                                       weight.saturating_add(get_output_weight(&tx_out.script_pubkey).to_wu())
+                               }),
+               )
+       }
+
+       fn received_tx_add_input(&mut self, msg: &msgs::TxAddInput) -> Result<(), AbortReason> {
+               // The interactive-txs spec calls for us to fail negotiation if the `prevtx` we receive is
+               // invalid. However, we would not need to account for this explicit negotiation failure
+               // mode here since `PeerManager` would already disconnect the peer if the `prevtx` is
+               // invalid; implicitly ending the negotiation.
+
+               if !self.is_serial_id_valid_for_counterparty(&msg.serial_id) {
+                       // The receiving node:
+                       //  - MUST fail the negotiation if:
+                       //     - the `serial_id` has the wrong parity
+                       return Err(AbortReason::IncorrectSerialIdParity);
+               }
+
+               self.received_tx_add_input_count += 1;
+               if self.received_tx_add_input_count > MAX_RECEIVED_TX_ADD_INPUT_COUNT {
+                       // The receiving node:
+                       //  - MUST fail the negotiation if:
+                       //     - if has received 4096 `tx_add_input` messages during this negotiation
+                       return Err(AbortReason::ReceivedTooManyTxAddInputs);
+               }
+
+               if msg.sequence >= 0xFFFFFFFE {
+                       // The receiving node:
+                       //  - MUST fail the negotiation if:
+                       //    - `sequence` is set to `0xFFFFFFFE` or `0xFFFFFFFF`
+                       return Err(AbortReason::IncorrectInputSequenceValue);
+               }
+
+               let transaction = msg.prevtx.as_transaction();
+               let txid = transaction.txid();
+
+               if let Some(tx_out) = transaction.output.get(msg.prevtx_out as usize) {
+                       if !tx_out.script_pubkey.is_witness_program() {
+                               // The receiving node:
+                               //  - MUST fail the negotiation if:
+                               //     - the `scriptPubKey` is not a witness program
+                               return Err(AbortReason::PrevTxOutInvalid);
+                       }
+
+                       if !self.prevtx_outpoints.insert(OutPoint { txid, vout: msg.prevtx_out }) {
+                               // The receiving node:
+                               //  - MUST fail the negotiation if:
+                               //     - the `prevtx` and `prevtx_vout` are identical to a previously added
+                               //       (and not removed) input's
+                               return Err(AbortReason::PrevTxOutInvalid);
+                       }
+               } else {
+                       // The receiving node:
+                       //  - MUST fail the negotiation if:
+                       //     - `prevtx_vout` is greater or equal to the number of outputs on `prevtx`
+                       return Err(AbortReason::PrevTxOutInvalid);
+               }
+
+               let prev_out = if let Some(prev_out) = transaction.output.get(msg.prevtx_out as usize) {
+                       prev_out.clone()
+               } else {
+                       return Err(AbortReason::PrevTxOutInvalid);
+               };
+               match self.inputs.entry(msg.serial_id) {
+                       hash_map::Entry::Occupied(_) => {
+                               // The receiving node:
+                               //  - MUST fail the negotiation if:
+                               //    - the `serial_id` is already included in the transaction
+                               Err(AbortReason::DuplicateSerialId)
+                       },
+                       hash_map::Entry::Vacant(entry) => {
+                               let prev_outpoint = OutPoint { txid, vout: msg.prevtx_out };
+                               entry.insert(InteractiveTxInput {
+                                       serial_id: msg.serial_id,
+                                       input: TxIn {
+                                               previous_output: prev_outpoint,
+                                               sequence: Sequence(msg.sequence),
+                                               ..Default::default()
+                                       },
+                                       prev_output: prev_out,
+                               });
+                               self.prevtx_outpoints.insert(prev_outpoint);
+                               Ok(())
+                       },
+               }
+       }
+
+       fn received_tx_remove_input(&mut self, msg: &msgs::TxRemoveInput) -> Result<(), AbortReason> {
+               if !self.is_serial_id_valid_for_counterparty(&msg.serial_id) {
+                       return Err(AbortReason::IncorrectSerialIdParity);
+               }
+
+               self.inputs
+                       .remove(&msg.serial_id)
+                       // The receiving node:
+                       //  - MUST fail the negotiation if:
+                       //    - the input or output identified by the `serial_id` was not added by the sender
+                       //    - the `serial_id` does not correspond to a currently added input
+                       .ok_or(AbortReason::SerialIdUnknown)
+                       .map(|_| ())
+       }
+
+       fn received_tx_add_output(&mut self, msg: &msgs::TxAddOutput) -> Result<(), AbortReason> {
+               // The receiving node:
+               //  - MUST fail the negotiation if:
+               //     - the serial_id has the wrong parity
+               if !self.is_serial_id_valid_for_counterparty(&msg.serial_id) {
+                       return Err(AbortReason::IncorrectSerialIdParity);
+               }
+
+               self.received_tx_add_output_count += 1;
+               if self.received_tx_add_output_count > MAX_RECEIVED_TX_ADD_OUTPUT_COUNT {
+                       // The receiving node:
+                       //  - MUST fail the negotiation if:
+                       //     - if has received 4096 `tx_add_output` messages during this negotiation
+                       return Err(AbortReason::ReceivedTooManyTxAddOutputs);
+               }
+
+               if msg.sats < msg.script.dust_value().to_sat() {
+                       // The receiving node:
+                       // - MUST fail the negotiation if:
+                       //              - the sats amount is less than the dust_limit
+                       return Err(AbortReason::BelowDustLimit);
+               }
+
+               // Check that adding this output would not cause the total output value to exceed the total
+               // bitcoin supply.
+               let mut outputs_value: u64 = 0;
+               for output in self.outputs.iter() {
+                       outputs_value = outputs_value.saturating_add(output.1.tx_out.value.to_sat());
+               }
+               if outputs_value.saturating_add(msg.sats) > TOTAL_BITCOIN_SUPPLY_SATOSHIS {
+                       // The receiving node:
+                       // - MUST fail the negotiation if:
+                       //              - the sats amount is greater than 2,100,000,000,000,000 (TOTAL_BITCOIN_SUPPLY_SATOSHIS)
+                       return Err(AbortReason::ExceededMaximumSatsAllowed);
+               }
+
+               // The receiving node:
+               //   - MUST accept P2WSH, P2WPKH, P2TR scripts
+               //   - MAY fail the negotiation if script is non-standard
+               //
+               // We can actually be a bit looser than the above as only witness version 0 has special
+               // length-based standardness constraints to match similar consensus rules. All witness scripts
+               // with witness versions V1 and up are always considered standard. Yes, the scripts can be
+               // anyone-can-spend-able, but if our counterparty wants to add an output like that then it's none
+               // of our concern really Â¯\_(ツ)_/¯
+               //
+               // TODO: The last check would be simplified when https://github.com/rust-bitcoin/rust-bitcoin/commit/1656e1a09a1959230e20af90d20789a4a8f0a31b
+               // hits the next release of rust-bitcoin.
+               if !(msg.script.is_p2wpkh()
+                       || msg.script.is_p2wsh()
+                       || (msg.script.is_witness_program()
+                               && msg.script.witness_version().map(|v| v.to_num() >= 1).unwrap_or(false)))
+               {
+                       return Err(AbortReason::InvalidOutputScript);
+               }
+
+               match self.outputs.entry(msg.serial_id) {
+                       hash_map::Entry::Occupied(_) => {
+                               // The receiving node:
+                               //  - MUST fail the negotiation if:
+                               //    - the `serial_id` is already included in the transaction
+                               Err(AbortReason::DuplicateSerialId)
+                       },
+                       hash_map::Entry::Vacant(entry) => {
+                               entry.insert(InteractiveTxOutput {
+                                       serial_id: msg.serial_id,
+                                       tx_out: TxOut {
+                                               value: Amount::from_sat(msg.sats),
+                                               script_pubkey: msg.script.clone(),
+                                       },
+                               });
+                               Ok(())
+                       },
+               }
+       }
+
+       fn received_tx_remove_output(&mut self, msg: &msgs::TxRemoveOutput) -> Result<(), AbortReason> {
+               if !self.is_serial_id_valid_for_counterparty(&msg.serial_id) {
+                       return Err(AbortReason::IncorrectSerialIdParity);
+               }
+               if self.outputs.remove(&msg.serial_id).is_some() {
+                       Ok(())
+               } else {
+                       // The receiving node:
+                       //  - MUST fail the negotiation if:
+                       //    - the input or output identified by the `serial_id` was not added by the sender
+                       //    - the `serial_id` does not correspond to a currently added input
+                       Err(AbortReason::SerialIdUnknown)
+               }
+       }
+
+       fn sent_tx_add_input(&mut self, msg: &msgs::TxAddInput) -> Result<(), AbortReason> {
+               let tx = msg.prevtx.as_transaction();
+               let input = TxIn {
+                       previous_output: OutPoint { txid: tx.txid(), vout: msg.prevtx_out },
+                       sequence: Sequence(msg.sequence),
+                       ..Default::default()
+               };
+               let prev_output =
+                       tx.output.get(msg.prevtx_out as usize).ok_or(AbortReason::PrevTxOutInvalid)?.clone();
+               if !self.prevtx_outpoints.insert(input.previous_output) {
+                       // We have added an input that already exists
+                       return Err(AbortReason::PrevTxOutInvalid);
+               }
+               self.inputs.insert(
+                       msg.serial_id,
+                       InteractiveTxInput { serial_id: msg.serial_id, input, prev_output },
+               );
+               Ok(())
+       }
+
+       fn sent_tx_add_output(&mut self, msg: &msgs::TxAddOutput) -> Result<(), AbortReason> {
+               self.outputs.insert(
+                       msg.serial_id,
+                       InteractiveTxOutput {
+                               serial_id: msg.serial_id,
+                               tx_out: TxOut {
+                                       value: Amount::from_sat(msg.sats),
+                                       script_pubkey: msg.script.clone(),
+                               },
+                       },
+               );
+               Ok(())
+       }
+
+       fn sent_tx_remove_input(&mut self, msg: &msgs::TxRemoveInput) -> Result<(), AbortReason> {
+               self.inputs.remove(&msg.serial_id);
+               Ok(())
+       }
+
+       fn sent_tx_remove_output(&mut self, msg: &msgs::TxRemoveOutput) -> Result<(), AbortReason> {
+               self.outputs.remove(&msg.serial_id);
+               Ok(())
+       }
+
+       fn check_counterparty_fees(
+               &self, counterparty_fees_contributed: u64,
+       ) -> Result<(), AbortReason> {
+               let counterparty_weight_contributed = self
+                       .remote_inputs_weight()
+                       .to_wu()
+                       .saturating_add(self.remote_outputs_weight().to_wu());
+               let mut required_counterparty_contribution_fee =
+                       fee_for_weight(self.feerate_sat_per_kw, counterparty_weight_contributed);
+               if !self.holder_is_initiator {
+                       // if is the non-initiator:
+                       //      - the initiator's fees do not cover the common fields (version, segwit marker + flag,
+                       //              input count, output count, locktime)
+                       let tx_common_fields_fee =
+                               fee_for_weight(self.feerate_sat_per_kw, TX_COMMON_FIELDS_WEIGHT);
+                       required_counterparty_contribution_fee += tx_common_fields_fee;
+               }
+               if counterparty_fees_contributed < required_counterparty_contribution_fee {
+                       return Err(AbortReason::InsufficientFees);
+               }
+               Ok(())
+       }
+
+       fn validate_tx(self) -> Result<ConstructedTransaction, AbortReason> {
+               // The receiving node:
+               // MUST fail the negotiation if:
+
+               // - the peer's total input satoshis is less than their outputs
+               let remote_inputs_value = self.remote_inputs_value();
+               let remote_outputs_value = self.remote_outputs_value();
+               if remote_inputs_value < remote_outputs_value {
+                       return Err(AbortReason::OutputsValueExceedsInputsValue);
+               }
+
+               // - there are more than 252 inputs
+               // - there are more than 252 outputs
+               if self.inputs.len() > MAX_INPUTS_OUTPUTS_COUNT
+                       || self.outputs.len() > MAX_INPUTS_OUTPUTS_COUNT
+               {
+                       return Err(AbortReason::ExceededNumberOfInputsOrOutputs);
+               }
+
+               // - the peer's paid feerate does not meet or exceed the agreed feerate (based on the minimum fee).
+               self.check_counterparty_fees(remote_inputs_value.saturating_sub(remote_outputs_value))?;
+
+               let constructed_tx = ConstructedTransaction::new(self);
+
+               if constructed_tx.weight().to_wu() > MAX_STANDARD_TX_WEIGHT as u64 {
+                       return Err(AbortReason::TransactionTooLarge);
+               }
+
+               Ok(constructed_tx)
+       }
+}
+
+// The interactive transaction construction protocol allows two peers to collaboratively build a
+// transaction for broadcast.
+//
+// The protocol is turn-based, so we define different states here that we store depending on whose
+// turn it is to send the next message. The states are defined so that their types ensure we only
+// perform actions (only send messages) via defined state transitions that do not violate the
+// protocol.
+//
+// An example of a full negotiation and associated states follows:
+//
+//     +------------+                         +------------------+---- Holder state after message sent/received ----+
+//     |            |--(1)- tx_add_input ---->|                  |                  SentChangeMsg                   +
+//     |            |<-(2)- tx_complete ------|                  |                ReceivedTxComplete                +
+//     |            |--(3)- tx_add_output --->|                  |                  SentChangeMsg                   +
+//     |            |<-(4)- tx_complete ------|                  |                ReceivedTxComplete                +
+//     |            |--(5)- tx_add_input ---->|                  |                  SentChangeMsg                   +
+//     |   Holder   |<-(6)- tx_add_input -----|   Counterparty   |                ReceivedChangeMsg                 +
+//     |            |--(7)- tx_remove_output >|                  |                  SentChangeMsg                   +
+//     |            |<-(8)- tx_add_output ----|                  |                ReceivedChangeMsg                 +
+//     |            |--(9)- tx_complete ----->|                  |                  SentTxComplete                  +
+//     |            |<-(10) tx_complete ------|                  |                NegotiationComplete               +
+//     +------------+                         +------------------+--------------------------------------------------+
+
+/// Negotiation states that can send & receive `tx_(add|remove)_(input|output)` and `tx_complete`
+trait State {}
+
+/// Category of states where we have sent some message to the counterparty, and we are waiting for
+/// a response.
+trait SentMsgState: State {
+       fn into_negotiation_context(self) -> NegotiationContext;
+}
+
+/// Category of states that our counterparty has put us in after we receive a message from them.
+trait ReceivedMsgState: State {
+       fn into_negotiation_context(self) -> NegotiationContext;
+}
+
+// This macro is a helper for implementing the above state traits for various states subsequently
+// defined below the macro.
+macro_rules! define_state {
+       (SENT_MSG_STATE, $state: ident, $doc: expr) => {
+               define_state!($state, NegotiationContext, $doc);
+               impl SentMsgState for $state {
+                       fn into_negotiation_context(self) -> NegotiationContext {
+                               self.0
+                       }
+               }
+       };
+       (RECEIVED_MSG_STATE, $state: ident, $doc: expr) => {
+               define_state!($state, NegotiationContext, $doc);
+               impl ReceivedMsgState for $state {
+                       fn into_negotiation_context(self) -> NegotiationContext {
+                               self.0
+                       }
+               }
+       };
+       ($state: ident, $inner: ident, $doc: expr) => {
+               #[doc = $doc]
+               #[derive(Debug)]
+               struct $state($inner);
+               impl State for $state {}
+       };
+}
+
+define_state!(
+       SENT_MSG_STATE,
+       SentChangeMsg,
+       "We have sent a message to the counterparty that has affected our negotiation state."
+);
+define_state!(
+       SENT_MSG_STATE,
+       SentTxComplete,
+       "We have sent a `tx_complete` message and are awaiting the counterparty's."
+);
+define_state!(
+       RECEIVED_MSG_STATE,
+       ReceivedChangeMsg,
+       "We have received a message from the counterparty that has affected our negotiation state."
+);
+define_state!(
+       RECEIVED_MSG_STATE,
+       ReceivedTxComplete,
+       "We have received a `tx_complete` message and the counterparty is awaiting ours."
+);
+define_state!(NegotiationComplete, ConstructedTransaction, "We have exchanged consecutive `tx_complete` messages with the counterparty and the transaction negotiation is complete.");
+define_state!(
+       NegotiationAborted,
+       AbortReason,
+       "The negotiation has failed and cannot be continued."
+);
+
+type StateTransitionResult<S> = Result<S, AbortReason>;
+
+trait StateTransition<NewState: State, TransitionData> {
+       fn transition(self, data: TransitionData) -> StateTransitionResult<NewState>;
+}
+
+// This macro helps define the legal transitions between the states above by implementing
+// the `StateTransition` trait for each of the states that follow this declaration.
+macro_rules! define_state_transitions {
+       (SENT_MSG_STATE, [$(DATA $data: ty, TRANSITION $transition: ident),+]) => {
+               $(
+                       impl<S: SentMsgState> StateTransition<ReceivedChangeMsg, $data> for S {
+                               fn transition(self, data: $data) -> StateTransitionResult<ReceivedChangeMsg> {
+                                       let mut context = self.into_negotiation_context();
+                                       context.$transition(data)?;
+                                       Ok(ReceivedChangeMsg(context))
+                               }
+                       }
+                )*
+       };
+       (RECEIVED_MSG_STATE, [$(DATA $data: ty, TRANSITION $transition: ident),+]) => {
+               $(
+                       impl<S: ReceivedMsgState> StateTransition<SentChangeMsg, $data> for S {
+                               fn transition(self, data: $data) -> StateTransitionResult<SentChangeMsg> {
+                                       let mut context = self.into_negotiation_context();
+                                       context.$transition(data)?;
+                                       Ok(SentChangeMsg(context))
+                               }
+                       }
+                )*
+       };
+       (TX_COMPLETE, $from_state: ident, $tx_complete_state: ident) => {
+               impl StateTransition<NegotiationComplete, &msgs::TxComplete> for $tx_complete_state {
+                       fn transition(self, _data: &msgs::TxComplete) -> StateTransitionResult<NegotiationComplete> {
+                               let context = self.into_negotiation_context();
+                               let tx = context.validate_tx()?;
+                               Ok(NegotiationComplete(tx))
+                       }
+               }
+
+               impl StateTransition<$tx_complete_state, &msgs::TxComplete> for $from_state {
+                       fn transition(self, _data: &msgs::TxComplete) -> StateTransitionResult<$tx_complete_state> {
+                               Ok($tx_complete_state(self.into_negotiation_context()))
+                       }
+               }
+       };
+}
+
+// State transitions when we have sent our counterparty some messages and are waiting for them
+// to respond.
+define_state_transitions!(SENT_MSG_STATE, [
+       DATA &msgs::TxAddInput, TRANSITION received_tx_add_input,
+       DATA &msgs::TxRemoveInput, TRANSITION received_tx_remove_input,
+       DATA &msgs::TxAddOutput, TRANSITION received_tx_add_output,
+       DATA &msgs::TxRemoveOutput, TRANSITION received_tx_remove_output
+]);
+// State transitions when we have received some messages from our counterparty and we should
+// respond.
+define_state_transitions!(RECEIVED_MSG_STATE, [
+       DATA &msgs::TxAddInput, TRANSITION sent_tx_add_input,
+       DATA &msgs::TxRemoveInput, TRANSITION sent_tx_remove_input,
+       DATA &msgs::TxAddOutput, TRANSITION sent_tx_add_output,
+       DATA &msgs::TxRemoveOutput, TRANSITION sent_tx_remove_output
+]);
+define_state_transitions!(TX_COMPLETE, SentChangeMsg, ReceivedTxComplete);
+define_state_transitions!(TX_COMPLETE, ReceivedChangeMsg, SentTxComplete);
+
+#[derive(Debug)]
+enum StateMachine {
+       Indeterminate,
+       SentChangeMsg(SentChangeMsg),
+       ReceivedChangeMsg(ReceivedChangeMsg),
+       SentTxComplete(SentTxComplete),
+       ReceivedTxComplete(ReceivedTxComplete),
+       NegotiationComplete(NegotiationComplete),
+       NegotiationAborted(NegotiationAborted),
+}
+
+impl Default for StateMachine {
+       fn default() -> Self {
+               Self::Indeterminate
+       }
+}
+
+// The `StateMachine` internally executes the actual transition between two states and keeps
+// track of the current state. This macro defines _how_ those state transitions happen to
+// update the internal state.
+macro_rules! define_state_machine_transitions {
+       ($transition: ident, $msg: ty, [$(FROM $from_state: ident, TO $to_state: ident),+]) => {
+               fn $transition(self, msg: $msg) -> StateMachine {
+                       match self {
+                               $(
+                                       Self::$from_state(s) => match s.transition(msg) {
+                                               Ok(new_state) => StateMachine::$to_state(new_state),
+                                               Err(abort_reason) => StateMachine::NegotiationAborted(NegotiationAborted(abort_reason)),
+                                       }
+                                )*
+                               _ => StateMachine::NegotiationAborted(NegotiationAborted(AbortReason::UnexpectedCounterpartyMessage)),
+                       }
+               }
+       };
+}
+
+impl StateMachine {
+       fn new(feerate_sat_per_kw: u32, is_initiator: bool, tx_locktime: AbsoluteLockTime) -> Self {
+               let context = NegotiationContext {
+                       tx_locktime,
+                       holder_is_initiator: is_initiator,
+                       received_tx_add_input_count: 0,
+                       received_tx_add_output_count: 0,
+                       inputs: new_hash_map(),
+                       prevtx_outpoints: new_hash_set(),
+                       outputs: new_hash_map(),
+                       feerate_sat_per_kw,
+               };
+               if is_initiator {
+                       Self::ReceivedChangeMsg(ReceivedChangeMsg(context))
+               } else {
+                       Self::SentChangeMsg(SentChangeMsg(context))
+               }
+       }
+
+       // TxAddInput
+       define_state_machine_transitions!(sent_tx_add_input, &msgs::TxAddInput, [
+               FROM ReceivedChangeMsg, TO SentChangeMsg,
+               FROM ReceivedTxComplete, TO SentChangeMsg
+       ]);
+       define_state_machine_transitions!(received_tx_add_input, &msgs::TxAddInput, [
+               FROM SentChangeMsg, TO ReceivedChangeMsg,
+               FROM SentTxComplete, TO ReceivedChangeMsg
+       ]);
+
+       // TxAddOutput
+       define_state_machine_transitions!(sent_tx_add_output, &msgs::TxAddOutput, [
+               FROM ReceivedChangeMsg, TO SentChangeMsg,
+               FROM ReceivedTxComplete, TO SentChangeMsg
+       ]);
+       define_state_machine_transitions!(received_tx_add_output, &msgs::TxAddOutput, [
+               FROM SentChangeMsg, TO ReceivedChangeMsg,
+               FROM SentTxComplete, TO ReceivedChangeMsg
+       ]);
+
+       // TxRemoveInput
+       define_state_machine_transitions!(sent_tx_remove_input, &msgs::TxRemoveInput, [
+               FROM ReceivedChangeMsg, TO SentChangeMsg,
+               FROM ReceivedTxComplete, TO SentChangeMsg
+       ]);
+       define_state_machine_transitions!(received_tx_remove_input, &msgs::TxRemoveInput, [
+               FROM SentChangeMsg, TO ReceivedChangeMsg,
+               FROM SentTxComplete, TO ReceivedChangeMsg
+       ]);
+
+       // TxRemoveOutput
+       define_state_machine_transitions!(sent_tx_remove_output, &msgs::TxRemoveOutput, [
+               FROM ReceivedChangeMsg, TO SentChangeMsg,
+               FROM ReceivedTxComplete, TO SentChangeMsg
+       ]);
+       define_state_machine_transitions!(received_tx_remove_output, &msgs::TxRemoveOutput, [
+               FROM SentChangeMsg, TO ReceivedChangeMsg,
+               FROM SentTxComplete, TO ReceivedChangeMsg
+       ]);
+
+       // TxComplete
+       define_state_machine_transitions!(sent_tx_complete, &msgs::TxComplete, [
+               FROM ReceivedChangeMsg, TO SentTxComplete,
+               FROM ReceivedTxComplete, TO NegotiationComplete
+       ]);
+       define_state_machine_transitions!(received_tx_complete, &msgs::TxComplete, [
+               FROM SentChangeMsg, TO ReceivedTxComplete,
+               FROM SentTxComplete, TO NegotiationComplete
+       ]);
+}
+
+pub(crate) struct InteractiveTxConstructor {
+       state_machine: StateMachine,
+       channel_id: ChannelId,
+       inputs_to_contribute: Vec<(SerialId, TxIn, TransactionU16LenLimited)>,
+       outputs_to_contribute: Vec<(SerialId, TxOut)>,
+}
+
+pub(crate) enum InteractiveTxMessageSend {
+       TxAddInput(msgs::TxAddInput),
+       TxAddOutput(msgs::TxAddOutput),
+       TxComplete(msgs::TxComplete),
+}
+
+// This macro executes a state machine transition based on a provided action.
+macro_rules! do_state_transition {
+       ($self: ident, $transition: ident, $msg: expr) => {{
+               let state_machine = core::mem::take(&mut $self.state_machine);
+               $self.state_machine = state_machine.$transition($msg);
+               match &$self.state_machine {
+                       StateMachine::NegotiationAborted(state) => Err(state.0.clone()),
+                       _ => Ok(()),
+               }
+       }};
+}
+
+fn generate_holder_serial_id<ES: Deref>(entropy_source: &ES, is_initiator: bool) -> SerialId
+where
+       ES::Target: EntropySource,
+{
+       let rand_bytes = entropy_source.get_secure_random_bytes();
+       let mut serial_id_bytes = [0u8; 8];
+       serial_id_bytes.copy_from_slice(&rand_bytes[..8]);
+       let mut serial_id = u64::from_be_bytes(serial_id_bytes);
+       if serial_id.is_for_initiator() != is_initiator {
+               serial_id ^= 1;
+       }
+       serial_id
+}
+
+pub(crate) enum HandleTxCompleteValue {
+       SendTxMessage(InteractiveTxMessageSend),
+       SendTxComplete(InteractiveTxMessageSend, ConstructedTransaction),
+       NegotiationComplete(ConstructedTransaction),
+}
+
+impl InteractiveTxConstructor {
+       /// Instantiates a new `InteractiveTxConstructor`.
+       ///
+       /// A tuple is returned containing the newly instantiate `InteractiveTxConstructor` and optionally
+       /// an initial wrapped `Tx_` message which the holder needs to send to the counterparty.
+       pub fn new<ES: Deref>(
+               entropy_source: &ES, channel_id: ChannelId, feerate_sat_per_kw: u32, is_initiator: bool,
+               funding_tx_locktime: AbsoluteLockTime,
+               inputs_to_contribute: Vec<(TxIn, TransactionU16LenLimited)>,
+               outputs_to_contribute: Vec<TxOut>,
+       ) -> (Self, Option<InteractiveTxMessageSend>)
+       where
+               ES::Target: EntropySource,
+       {
+               let state_machine =
+                       StateMachine::new(feerate_sat_per_kw, is_initiator, funding_tx_locktime);
+               let mut inputs_to_contribute: Vec<(SerialId, TxIn, TransactionU16LenLimited)> =
+                       inputs_to_contribute
+                               .into_iter()
+                               .map(|(input, tx)| {
+                                       let serial_id = generate_holder_serial_id(entropy_source, is_initiator);
+                                       (serial_id, input, tx)
+                               })
+                               .collect();
+               // We'll sort by the randomly generated serial IDs, effectively shuffling the order of the inputs
+               // as the user passed them to us to avoid leaking any potential categorization of transactions
+               // before we pass any of the inputs to the counterparty.
+               inputs_to_contribute.sort_unstable_by_key(|(serial_id, _, _)| *serial_id);
+               let mut outputs_to_contribute: Vec<(SerialId, TxOut)> = outputs_to_contribute
+                       .into_iter()
+                       .map(|output| {
+                               let serial_id = generate_holder_serial_id(entropy_source, is_initiator);
+                               (serial_id, output)
+                       })
+                       .collect();
+               // In the same manner and for the same rationale as the inputs above, we'll shuffle the outputs.
+               outputs_to_contribute.sort_unstable_by_key(|(serial_id, _)| *serial_id);
+               let mut constructor =
+                       Self { state_machine, channel_id, inputs_to_contribute, outputs_to_contribute };
+               let message_send = if is_initiator {
+                       match constructor.maybe_send_message() {
+                               Ok(msg_send) => Some(msg_send),
+                               Err(_) => {
+                                       debug_assert!(
+                                               false,
+                                               "We should always be able to start our state machine successfully"
+                                       );
+                                       None
+                               },
+                       }
+               } else {
+                       None
+               };
+               (constructor, message_send)
+       }
+
+       fn maybe_send_message(&mut self) -> Result<InteractiveTxMessageSend, AbortReason> {
+               // We first attempt to send inputs we want to add, then outputs. Once we are done sending
+               // them both, then we always send tx_complete.
+               if let Some((serial_id, input, prevtx)) = self.inputs_to_contribute.pop() {
+                       let msg = msgs::TxAddInput {
+                               channel_id: self.channel_id,
+                               serial_id,
+                               prevtx,
+                               prevtx_out: input.previous_output.vout,
+                               sequence: input.sequence.to_consensus_u32(),
+                       };
+                       do_state_transition!(self, sent_tx_add_input, &msg)?;
+                       Ok(InteractiveTxMessageSend::TxAddInput(msg))
+               } else if let Some((serial_id, output)) = self.outputs_to_contribute.pop() {
+                       let msg = msgs::TxAddOutput {
+                               channel_id: self.channel_id,
+                               serial_id,
+                               sats: output.value.to_sat(),
+                               script: output.script_pubkey,
+                       };
+                       do_state_transition!(self, sent_tx_add_output, &msg)?;
+                       Ok(InteractiveTxMessageSend::TxAddOutput(msg))
+               } else {
+                       let msg = msgs::TxComplete { channel_id: self.channel_id };
+                       do_state_transition!(self, sent_tx_complete, &msg)?;
+                       Ok(InteractiveTxMessageSend::TxComplete(msg))
+               }
+       }
+
+       pub fn handle_tx_add_input(
+               &mut self, msg: &msgs::TxAddInput,
+       ) -> Result<InteractiveTxMessageSend, AbortReason> {
+               do_state_transition!(self, received_tx_add_input, msg)?;
+               self.maybe_send_message()
+       }
+
+       pub fn handle_tx_remove_input(
+               &mut self, msg: &msgs::TxRemoveInput,
+       ) -> Result<InteractiveTxMessageSend, AbortReason> {
+               do_state_transition!(self, received_tx_remove_input, msg)?;
+               self.maybe_send_message()
+       }
+
+       pub fn handle_tx_add_output(
+               &mut self, msg: &msgs::TxAddOutput,
+       ) -> Result<InteractiveTxMessageSend, AbortReason> {
+               do_state_transition!(self, received_tx_add_output, msg)?;
+               self.maybe_send_message()
+       }
+
+       pub fn handle_tx_remove_output(
+               &mut self, msg: &msgs::TxRemoveOutput,
+       ) -> Result<InteractiveTxMessageSend, AbortReason> {
+               do_state_transition!(self, received_tx_remove_output, msg)?;
+               self.maybe_send_message()
+       }
+
+       pub fn handle_tx_complete(
+               &mut self, msg: &msgs::TxComplete,
+       ) -> Result<HandleTxCompleteValue, AbortReason> {
+               do_state_transition!(self, received_tx_complete, msg)?;
+               match &self.state_machine {
+                       StateMachine::ReceivedTxComplete(_) => {
+                               let msg_send = self.maybe_send_message()?;
+                               match &self.state_machine {
+                                       StateMachine::NegotiationComplete(s) => {
+                                               Ok(HandleTxCompleteValue::SendTxComplete(msg_send, s.0.clone()))
+                                       },
+                                       StateMachine::SentChangeMsg(_) => {
+                                               Ok(HandleTxCompleteValue::SendTxMessage(msg_send))
+                                       }, // We either had an input or output to contribute.
+                                       _ => {
+                                               debug_assert!(false, "We cannot transition to any other states after receiving `tx_complete` and responding");
+                                               Err(AbortReason::InvalidStateTransition)
+                                       },
+                               }
+                       },
+                       StateMachine::NegotiationComplete(s) => {
+                               Ok(HandleTxCompleteValue::NegotiationComplete(s.0.clone()))
+                       },
+                       _ => {
+                               debug_assert!(
+                                       false,
+                                       "We cannot transition to any other states after receiving `tx_complete`"
+                               );
+                               Err(AbortReason::InvalidStateTransition)
+                       },
+               }
+       }
+}
+
+#[cfg(test)]
+mod tests {
+       use crate::chain::chaininterface::{fee_for_weight, FEERATE_FLOOR_SATS_PER_KW};
+       use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
+       use crate::ln::interactivetxs::{
+               generate_holder_serial_id, AbortReason, HandleTxCompleteValue, InteractiveTxConstructor,
+               InteractiveTxMessageSend, MAX_INPUTS_OUTPUTS_COUNT, MAX_RECEIVED_TX_ADD_INPUT_COUNT,
+               MAX_RECEIVED_TX_ADD_OUTPUT_COUNT,
+       };
+       use crate::ln::types::ChannelId;
+       use crate::sign::EntropySource;
+       use crate::util::atomic_counter::AtomicCounter;
+       use crate::util::ser::TransactionU16LenLimited;
+       use bitcoin::amount::Amount;
+       use bitcoin::blockdata::opcodes;
+       use bitcoin::blockdata::script::Builder;
+       use bitcoin::hashes::Hash;
+       use bitcoin::key::UntweakedPublicKey;
+       use bitcoin::secp256k1::{Keypair, Secp256k1};
+       use bitcoin::transaction::Version;
+       use bitcoin::{
+               absolute::LockTime as AbsoluteLockTime, OutPoint, Sequence, Transaction, TxIn, TxOut,
+       };
+       use bitcoin::{PubkeyHash, ScriptBuf, WPubkeyHash, WScriptHash};
+       use core::ops::Deref;
+
+       use super::{
+               get_output_weight, P2TR_INPUT_WEIGHT_LOWER_BOUND, P2WPKH_INPUT_WEIGHT_LOWER_BOUND,
+               P2WSH_INPUT_WEIGHT_LOWER_BOUND, TX_COMMON_FIELDS_WEIGHT,
+       };
+
+       const TEST_FEERATE_SATS_PER_KW: u32 = FEERATE_FLOOR_SATS_PER_KW * 10;
+
+       // A simple entropy source that works based on an atomic counter.
+       struct TestEntropySource(AtomicCounter);
+       impl EntropySource for TestEntropySource {
+               fn get_secure_random_bytes(&self) -> [u8; 32] {
+                       let mut res = [0u8; 32];
+                       let increment = self.0.get_increment();
+                       for i in 0..32 {
+                               // Rotate the increment value by 'i' bits to the right, to avoid clashes
+                               // when `generate_local_serial_id` does a parity flip on consecutive calls for the
+                               // same party.
+                               let rotated_increment = increment.rotate_right(i as u32);
+                               res[i] = (rotated_increment & 0xff) as u8;
+                       }
+                       res
+               }
+       }
+
+       // An entropy source that deliberately returns you the same seed every time. We use this
+       // to test if the constructor would catch inputs/outputs that are attempting to be added
+       // with duplicate serial ids.
+       struct DuplicateEntropySource;
+       impl EntropySource for DuplicateEntropySource {
+               fn get_secure_random_bytes(&self) -> [u8; 32] {
+                       let mut res = [0u8; 32];
+                       let count = 1u64;
+                       res[0..8].copy_from_slice(&count.to_be_bytes());
+                       res
+               }
+       }
+
+       #[derive(Debug, PartialEq, Eq)]
+       enum ErrorCulprit {
+               NodeA,
+               NodeB,
+               // Some error values are only checked at the end of the negotiation and are not easy to attribute
+               // to a particular party. Both parties would indicate an `AbortReason` in this case.
+               // e.g. Exceeded max inputs and outputs after negotiation.
+               Indeterminate,
+       }
+
+       struct TestSession {
+               description: &'static str,
+               inputs_a: Vec<(TxIn, TransactionU16LenLimited)>,
+               outputs_a: Vec<TxOut>,
+               inputs_b: Vec<(TxIn, TransactionU16LenLimited)>,
+               outputs_b: Vec<TxOut>,
+               expect_error: Option<(AbortReason, ErrorCulprit)>,
+       }
+
+       fn do_test_interactive_tx_constructor(session: TestSession) {
+               let entropy_source = TestEntropySource(AtomicCounter::new());
+               do_test_interactive_tx_constructor_internal(session, &&entropy_source);
+       }
+
+       fn do_test_interactive_tx_constructor_with_entropy_source<ES: Deref>(
+               session: TestSession, entropy_source: ES,
+       ) where
+               ES::Target: EntropySource,
+       {
+               do_test_interactive_tx_constructor_internal(session, &entropy_source);
+       }
+
+       fn do_test_interactive_tx_constructor_internal<ES: Deref>(
+               session: TestSession, entropy_source: &ES,
+       ) where
+               ES::Target: EntropySource,
+       {
+               let channel_id = ChannelId(entropy_source.get_secure_random_bytes());
+               let tx_locktime = AbsoluteLockTime::from_height(1337).unwrap();
+
+               let (mut constructor_a, first_message_a) = InteractiveTxConstructor::new(
+                       entropy_source,
+                       channel_id,
+                       TEST_FEERATE_SATS_PER_KW,
+                       true,
+                       tx_locktime,
+                       session.inputs_a,
+                       session.outputs_a,
+               );
+               let (mut constructor_b, first_message_b) = InteractiveTxConstructor::new(
+                       entropy_source,
+                       channel_id,
+                       TEST_FEERATE_SATS_PER_KW,
+                       false,
+                       tx_locktime,
+                       session.inputs_b,
+                       session.outputs_b,
+               );
+
+               let handle_message_send =
+                       |msg: InteractiveTxMessageSend, for_constructor: &mut InteractiveTxConstructor| {
+                               match msg {
+                                       InteractiveTxMessageSend::TxAddInput(msg) => for_constructor
+                                               .handle_tx_add_input(&msg)
+                                               .map(|msg_send| (Some(msg_send), None)),
+                                       InteractiveTxMessageSend::TxAddOutput(msg) => for_constructor
+                                               .handle_tx_add_output(&msg)
+                                               .map(|msg_send| (Some(msg_send), None)),
+                                       InteractiveTxMessageSend::TxComplete(msg) => {
+                                               for_constructor.handle_tx_complete(&msg).map(|value| match value {
+                                                       HandleTxCompleteValue::SendTxMessage(msg_send) => {
+                                                               (Some(msg_send), None)
+                                                       },
+                                                       HandleTxCompleteValue::SendTxComplete(msg_send, tx) => {
+                                                               (Some(msg_send), Some(tx))
+                                                       },
+                                                       HandleTxCompleteValue::NegotiationComplete(tx) => (None, Some(tx)),
+                                               })
+                                       },
+                               }
+                       };
+
+               assert!(first_message_b.is_none());
+               let mut message_send_a = first_message_a;
+               let mut message_send_b = None;
+               let mut final_tx_a = None;
+               let mut final_tx_b = None;
+               while final_tx_a.is_none() || final_tx_b.is_none() {
+                       if let Some(message_send_a) = message_send_a.take() {
+                               match handle_message_send(message_send_a, &mut constructor_b) {
+                                       Ok((msg_send, final_tx)) => {
+                                               message_send_b = msg_send;
+                                               final_tx_b = final_tx;
+                                       },
+                                       Err(abort_reason) => {
+                                               let error_culprit = match abort_reason {
+                                                       AbortReason::ExceededNumberOfInputsOrOutputs => {
+                                                               ErrorCulprit::Indeterminate
+                                                       },
+                                                       _ => ErrorCulprit::NodeA,
+                                               };
+                                               assert_eq!(
+                                                       Some((abort_reason, error_culprit)),
+                                                       session.expect_error,
+                                                       "Test: {}",
+                                                       session.description
+                                               );
+                                               assert!(message_send_b.is_none());
+                                               return;
+                                       },
+                               }
+                       }
+                       if let Some(message_send_b) = message_send_b.take() {
+                               match handle_message_send(message_send_b, &mut constructor_a) {
+                                       Ok((msg_send, final_tx)) => {
+                                               message_send_a = msg_send;
+                                               final_tx_a = final_tx;
+                                       },
+                                       Err(abort_reason) => {
+                                               let error_culprit = match abort_reason {
+                                                       AbortReason::ExceededNumberOfInputsOrOutputs => {
+                                                               ErrorCulprit::Indeterminate
+                                                       },
+                                                       _ => ErrorCulprit::NodeB,
+                                               };
+                                               assert_eq!(
+                                                       Some((abort_reason, error_culprit)),
+                                                       session.expect_error,
+                                                       "Test: {}",
+                                                       session.description
+                                               );
+                                               assert!(message_send_a.is_none());
+                                               return;
+                                       },
+                               }
+                       }
+               }
+               assert!(message_send_a.is_none());
+               assert!(message_send_b.is_none());
+               assert_eq!(final_tx_a.unwrap().into_unsigned_tx(), final_tx_b.unwrap().into_unsigned_tx());
+               assert!(session.expect_error.is_none(), "Test: {}", session.description);
+       }
+
+       #[derive(Debug, Clone, Copy)]
+       enum TestOutput {
+               P2WPKH(u64),
+               P2WSH(u64),
+               P2TR(u64),
+               // Non-witness type to test rejection.
+               P2PKH(u64),
+       }
+
+       fn generate_tx(outputs: &[TestOutput]) -> Transaction {
+               generate_tx_with_locktime(outputs, 1337)
+       }
+
+       fn generate_txout(output: &TestOutput) -> TxOut {
+               let secp_ctx = Secp256k1::new();
+               let (value, script_pubkey) = match output {
+                       TestOutput::P2WPKH(value) => {
+                               (*value, ScriptBuf::new_p2wpkh(&WPubkeyHash::from_slice(&[1; 20]).unwrap()))
+                       },
+                       TestOutput::P2WSH(value) => {
+                               (*value, ScriptBuf::new_p2wsh(&WScriptHash::from_slice(&[2; 32]).unwrap()))
+                       },
+                       TestOutput::P2TR(value) => (
+                               *value,
+                               ScriptBuf::new_p2tr(
+                                       &secp_ctx,
+                                       UntweakedPublicKey::from_keypair(
+                                               &Keypair::from_seckey_slice(&secp_ctx, &[3; 32]).unwrap(),
+                                       )
+                                       .0,
+                                       None,
+                               ),
+                       ),
+                       TestOutput::P2PKH(value) => {
+                               (*value, ScriptBuf::new_p2pkh(&PubkeyHash::from_slice(&[4; 20]).unwrap()))
+                       },
+               };
+
+               TxOut { value: Amount::from_sat(value), script_pubkey }
+       }
+
+       fn generate_tx_with_locktime(outputs: &[TestOutput], locktime: u32) -> Transaction {
+               Transaction {
+                       version: Version::TWO,
+                       lock_time: AbsoluteLockTime::from_height(locktime).unwrap(),
+                       input: vec![TxIn { ..Default::default() }],
+                       output: outputs.iter().map(generate_txout).collect(),
+               }
+       }
+
+       fn generate_inputs(outputs: &[TestOutput]) -> Vec<(TxIn, TransactionU16LenLimited)> {
+               let tx = generate_tx(outputs);
+               let txid = tx.txid();
+               tx.output
+                       .iter()
+                       .enumerate()
+                       .map(|(idx, _)| {
+                               let input = TxIn {
+                                       previous_output: OutPoint { txid, vout: idx as u32 },
+                                       script_sig: Default::default(),
+                                       sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
+                                       witness: Default::default(),
+                               };
+                               (input, TransactionU16LenLimited::new(tx.clone()).unwrap())
+                       })
+                       .collect()
+       }
+
+       fn generate_p2wsh_script_pubkey() -> ScriptBuf {
+               Builder::new().push_opcode(opcodes::OP_TRUE).into_script().to_p2wsh()
+       }
+
+       fn generate_p2wpkh_script_pubkey() -> ScriptBuf {
+               ScriptBuf::new_p2wpkh(&WPubkeyHash::from_slice(&[1; 20]).unwrap())
+       }
+
+       fn generate_outputs(outputs: &[TestOutput]) -> Vec<TxOut> {
+               outputs.iter().map(generate_txout).collect()
+       }
+
+       fn generate_fixed_number_of_inputs(count: u16) -> Vec<(TxIn, TransactionU16LenLimited)> {
+               // Generate transactions with a total `count` number of outputs such that no transaction has a
+               // serialized length greater than u16::MAX.
+               let max_outputs_per_prevtx = 1_500;
+               let mut remaining = count;
+               let mut inputs: Vec<(TxIn, TransactionU16LenLimited)> = Vec::with_capacity(count as usize);
+
+               while remaining > 0 {
+                       let tx_output_count = remaining.min(max_outputs_per_prevtx);
+                       remaining -= tx_output_count;
+
+                       // Use unique locktime for each tx so outpoints are different across transactions
+                       let tx = generate_tx_with_locktime(
+                               &vec![TestOutput::P2WPKH(1_000_000); tx_output_count as usize],
+                               (1337 + remaining).into(),
+                       );
+                       let txid = tx.txid();
+
+                       let mut temp: Vec<(TxIn, TransactionU16LenLimited)> = tx
+                               .output
+                               .iter()
+                               .enumerate()
+                               .map(|(idx, _)| {
+                                       let input = TxIn {
+                                               previous_output: OutPoint { txid, vout: idx as u32 },
+                                               script_sig: Default::default(),
+                                               sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
+                                               witness: Default::default(),
+                                       };
+                                       (input, TransactionU16LenLimited::new(tx.clone()).unwrap())
+                               })
+                               .collect();
+
+                       inputs.append(&mut temp);
+               }
+
+               inputs
+       }
+
+       fn generate_fixed_number_of_outputs(count: u16) -> Vec<TxOut> {
+               // Set a constant value for each TxOut
+               generate_outputs(&vec![TestOutput::P2WPKH(1_000_000); count as usize])
+       }
+
+       fn generate_p2sh_script_pubkey() -> ScriptBuf {
+               Builder::new().push_opcode(opcodes::OP_TRUE).into_script().to_p2sh()
+       }
+
+       fn generate_non_witness_output(value: u64) -> TxOut {
+               TxOut { value: Amount::from_sat(value), script_pubkey: generate_p2sh_script_pubkey() }
+       }
+
+       #[test]
+       fn test_interactive_tx_constructor() {
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "No contributions",
+                       inputs_a: vec![],
+                       outputs_a: vec![],
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Single contribution, no initiator inputs",
+                       inputs_a: vec![],
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(1_000_000)]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::OutputsValueExceedsInputsValue, ErrorCulprit::NodeA)),
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Single contribution, no initiator outputs",
+                       inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
+                       outputs_a: vec![],
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: None,
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Single contribution, no fees",
+                       inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(1_000_000)]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
+               });
+               let p2wpkh_fee = fee_for_weight(TEST_FEERATE_SATS_PER_KW, P2WPKH_INPUT_WEIGHT_LOWER_BOUND);
+               let outputs_fee = fee_for_weight(
+                       TEST_FEERATE_SATS_PER_KW,
+                       get_output_weight(&generate_p2wpkh_script_pubkey()).to_wu(),
+               );
+               let tx_common_fields_fee =
+                       fee_for_weight(TEST_FEERATE_SATS_PER_KW, TX_COMMON_FIELDS_WEIGHT);
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Single contribution, with P2WPKH input, insufficient fees",
+                       inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(
+                               1_000_000 - p2wpkh_fee - outputs_fee - tx_common_fields_fee + 1, /* makes fees insuffcient for initiator */
+                       )]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Single contribution with P2WPKH input, sufficient fees",
+                       inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(
+                               1_000_000 - p2wpkh_fee - outputs_fee - tx_common_fields_fee,
+                       )]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: None,
+               });
+               let p2wsh_fee = fee_for_weight(TEST_FEERATE_SATS_PER_KW, P2WSH_INPUT_WEIGHT_LOWER_BOUND);
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Single contribution, with P2WSH input, insufficient fees",
+                       inputs_a: generate_inputs(&[TestOutput::P2WSH(1_000_000)]),
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(
+                               1_000_000 - p2wsh_fee - outputs_fee - tx_common_fields_fee + 1, /* makes fees insuffcient for initiator */
+                       )]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Single contribution with P2WSH input, sufficient fees",
+                       inputs_a: generate_inputs(&[TestOutput::P2WSH(1_000_000)]),
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(
+                               1_000_000 - p2wsh_fee - outputs_fee - tx_common_fields_fee,
+                       )]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: None,
+               });
+               let p2tr_fee = fee_for_weight(TEST_FEERATE_SATS_PER_KW, P2TR_INPUT_WEIGHT_LOWER_BOUND);
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Single contribution, with P2TR input, insufficient fees",
+                       inputs_a: generate_inputs(&[TestOutput::P2TR(1_000_000)]),
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(
+                               1_000_000 - p2tr_fee - outputs_fee - tx_common_fields_fee + 1, /* makes fees insuffcient for initiator */
+                       )]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Single contribution with P2TR input, sufficient fees",
+                       inputs_a: generate_inputs(&[TestOutput::P2TR(1_000_000)]),
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(
+                               1_000_000 - p2tr_fee - outputs_fee - tx_common_fields_fee,
+                       )]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: None,
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Initiator contributes sufficient fees, but non-initiator does not",
+                       inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
+                       outputs_a: vec![],
+                       inputs_b: generate_inputs(&[TestOutput::P2WPKH(100_000)]),
+                       outputs_b: generate_outputs(&[TestOutput::P2WPKH(100_000)]),
+                       expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeB)),
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Multi-input-output contributions from both sides",
+                       inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000); 2]),
+                       outputs_a: generate_outputs(&[
+                               TestOutput::P2WPKH(1_000_000),
+                               TestOutput::P2WPKH(200_000),
+                       ]),
+                       inputs_b: generate_inputs(&[
+                               TestOutput::P2WPKH(1_000_000),
+                               TestOutput::P2WPKH(500_000),
+                       ]),
+                       outputs_b: generate_outputs(&[
+                               TestOutput::P2WPKH(1_000_000),
+                               TestOutput::P2WPKH(400_000),
+                       ]),
+                       expect_error: None,
+               });
+
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Prevout from initiator is not a witness program",
+                       inputs_a: generate_inputs(&[TestOutput::P2PKH(1_000_000)]),
+                       outputs_a: vec![],
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeA)),
+               });
+
+               let tx =
+                       TransactionU16LenLimited::new(generate_tx(&[TestOutput::P2WPKH(1_000_000)])).unwrap();
+               let invalid_sequence_input = TxIn {
+                       previous_output: OutPoint { txid: tx.as_transaction().txid(), vout: 0 },
+                       ..Default::default()
+               };
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Invalid input sequence from initiator",
+                       inputs_a: vec![(invalid_sequence_input, tx.clone())],
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(1_000_000)]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::IncorrectInputSequenceValue, ErrorCulprit::NodeA)),
+               });
+               let duplicate_input = TxIn {
+                       previous_output: OutPoint { txid: tx.as_transaction().txid(), vout: 0 },
+                       sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
+                       ..Default::default()
+               };
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Duplicate prevout from initiator",
+                       inputs_a: vec![(duplicate_input.clone(), tx.clone()), (duplicate_input, tx.clone())],
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(1_000_000)]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeB)),
+               });
+               let duplicate_input = TxIn {
+                       previous_output: OutPoint { txid: tx.as_transaction().txid(), vout: 0 },
+                       sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
+                       ..Default::default()
+               };
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Non-initiator uses same prevout as initiator",
+                       inputs_a: vec![(duplicate_input.clone(), tx.clone())],
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(1_000_000)]),
+                       inputs_b: vec![(duplicate_input.clone(), tx.clone())],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeA)),
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Initiator sends too many TxAddInputs",
+                       inputs_a: generate_fixed_number_of_inputs(MAX_RECEIVED_TX_ADD_INPUT_COUNT + 1),
+                       outputs_a: vec![],
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::ReceivedTooManyTxAddInputs, ErrorCulprit::NodeA)),
+               });
+               do_test_interactive_tx_constructor_with_entropy_source(
+                       TestSession {
+                               // We use a deliberately bad entropy source, `DuplicateEntropySource` to simulate this.
+                               description: "Attempt to queue up two inputs with duplicate serial ids",
+                               inputs_a: generate_fixed_number_of_inputs(2),
+                               outputs_a: vec![],
+                               inputs_b: vec![],
+                               outputs_b: vec![],
+                               expect_error: Some((AbortReason::DuplicateSerialId, ErrorCulprit::NodeA)),
+                       },
+                       &DuplicateEntropySource,
+               );
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Initiator sends too many TxAddOutputs",
+                       inputs_a: vec![],
+                       outputs_a: generate_fixed_number_of_outputs(MAX_RECEIVED_TX_ADD_OUTPUT_COUNT + 1),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::ReceivedTooManyTxAddOutputs, ErrorCulprit::NodeA)),
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Initiator sends an output below dust value",
+                       inputs_a: vec![],
+                       outputs_a: generate_outputs(&[TestOutput::P2WSH(
+                               generate_p2wsh_script_pubkey().dust_value().to_sat() - 1,
+                       )]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::BelowDustLimit, ErrorCulprit::NodeA)),
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Initiator sends an output above maximum sats allowed",
+                       inputs_a: vec![],
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(TOTAL_BITCOIN_SUPPLY_SATOSHIS + 1)]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::ExceededMaximumSatsAllowed, ErrorCulprit::NodeA)),
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Initiator sends an output without a witness program",
+                       inputs_a: vec![],
+                       outputs_a: vec![generate_non_witness_output(1_000_000)],
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::InvalidOutputScript, ErrorCulprit::NodeA)),
+               });
+               do_test_interactive_tx_constructor_with_entropy_source(
+                       TestSession {
+                               // We use a deliberately bad entropy source, `DuplicateEntropySource` to simulate this.
+                               description: "Attempt to queue up two outputs with duplicate serial ids",
+                               inputs_a: vec![],
+                               outputs_a: generate_fixed_number_of_outputs(2),
+                               inputs_b: vec![],
+                               outputs_b: vec![],
+                               expect_error: Some((AbortReason::DuplicateSerialId, ErrorCulprit::NodeA)),
+                       },
+                       &DuplicateEntropySource,
+               );
+
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Peer contributed more output value than inputs",
+                       inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000)]),
+                       outputs_a: generate_outputs(&[TestOutput::P2WPKH(1_000_000)]),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((AbortReason::OutputsValueExceedsInputsValue, ErrorCulprit::NodeA)),
+               });
+
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Peer contributed more than allowed number of inputs",
+                       inputs_a: generate_fixed_number_of_inputs(MAX_INPUTS_OUTPUTS_COUNT as u16 + 1),
+                       outputs_a: vec![],
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((
+                               AbortReason::ExceededNumberOfInputsOrOutputs,
+                               ErrorCulprit::Indeterminate,
+                       )),
+               });
+               do_test_interactive_tx_constructor(TestSession {
+                       description: "Peer contributed more than allowed number of outputs",
+                       inputs_a: generate_inputs(&[TestOutput::P2WPKH(TOTAL_BITCOIN_SUPPLY_SATOSHIS)]),
+                       outputs_a: generate_fixed_number_of_outputs(MAX_INPUTS_OUTPUTS_COUNT as u16 + 1),
+                       inputs_b: vec![],
+                       outputs_b: vec![],
+                       expect_error: Some((
+                               AbortReason::ExceededNumberOfInputsOrOutputs,
+                               ErrorCulprit::Indeterminate,
+                       )),
+               });
+       }
+
+       #[test]
+       fn test_generate_local_serial_id() {
+               let entropy_source = TestEntropySource(AtomicCounter::new());
+
+               // Initiators should have even serial id, non-initiators should have odd serial id.
+               assert_eq!(generate_holder_serial_id(&&entropy_source, true) % 2, 0);
+               assert_eq!(generate_holder_serial_id(&&entropy_source, false) % 2, 1)
+       }
+}
diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs
new file mode 100644 (file)
index 0000000..7bd605c
--- /dev/null
@@ -0,0 +1,342 @@
+// 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.
+
+//! Tests for calculating the maximum length of a path based on the payment metadata, custom TLVs,
+//! and/or blinded paths present.
+
+use bitcoin::secp256k1::Secp256k1;
+use crate::blinded_path::BlindedPath;
+use crate::blinded_path::payment::{PaymentConstraints, PaymentContext, ReceiveTlvs};
+use crate::events::MessageSendEventsProvider;
+use crate::ln::PaymentSecret;
+use crate::ln::blinded_payment_tests::get_blinded_route_parameters;
+use crate::ln::channelmanager::PaymentId;
+use crate::ln::functional_test_utils::*;
+use crate::ln::msgs;
+use crate::ln::onion_utils;
+use crate::ln::onion_utils::MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY;
+use crate::ln::outbound_payment::{RecipientOnionFields, Retry, RetryableSendFailure};
+use crate::prelude::*;
+use crate::routing::router::{DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, PaymentParameters, RouteParameters};
+use crate::util::errors::APIError;
+use crate::util::ser::Writeable;
+use crate::util::test_utils;
+
+// 3+32 (payload length and HMAC) + 2+8 (amt_to_forward) +
+// 2+4 (outgoing_cltv_value) + 2+8 (short_channel_id)
+const INTERMED_PAYLOAD_LEN_ESTIMATE: usize = 61;
+
+// Length of the HMAC of an onion payload when encoded into the packet.
+const PAYLOAD_HMAC_LEN: usize = 32;
+
+#[test]
+fn large_payment_metadata() {
+       // Test that we'll limit our maximum path length based on the size of the provided
+       // payment_metadata, and refuse to send at all prior to pathfinding if it's too large.
+       let chanmon_cfgs = create_chanmon_cfgs(3);
+       let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+       let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+       create_announced_chan_between_nodes(&nodes, 0, 1);
+       create_announced_chan_between_nodes(&nodes, 1, 2);
+
+       let amt_msat = 100_000;
+
+       // Construct payment_metadata such that we can send the payment to the next hop but no further
+       // without exceeding the max onion packet size.
+       let final_payload_len_without_metadata = msgs::OutboundOnionPayload::Receive {
+               payment_data: Some(msgs::FinalOnionHopData {
+                       payment_secret: PaymentSecret([0; 32]), total_msat: MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY
+               }),
+               payment_metadata: None,
+               keysend_preimage: None,
+               custom_tlvs: &Vec::new(),
+               sender_intended_htlc_amt_msat: MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY,
+               cltv_expiry_height: nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
+       }.serialized_length();
+       let max_metadata_len = 1300
+               - 1 // metadata type
+               - crate::util::ser::BigSize(1200).serialized_length() // metadata length
+               - 2 // onion payload varint prefix increased ser size due to metadata
+               - PAYLOAD_HMAC_LEN
+               - final_payload_len_without_metadata;
+       let mut payment_metadata = vec![42; max_metadata_len];
+
+       // Check that the maximum-size metadata is sendable.
+       let (mut route_0_1, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(&nodes[0], &nodes[1], amt_msat);
+       let mut recipient_onion_max_md_size = RecipientOnionFields {
+               payment_secret: Some(payment_secret),
+               payment_metadata: Some(payment_metadata.clone()),
+               custom_tlvs: Vec::new(),
+       };
+       nodes[0].node.send_payment(payment_hash, recipient_onion_max_md_size.clone(), PaymentId(payment_hash.0), route_0_1.route_params.clone().unwrap(), Retry::Attempts(0)).unwrap();
+       check_added_monitors!(nodes[0], 1);
+       let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+       assert_eq!(events.len(), 1);
+       let path = &[&nodes[1]];
+       let args = PassAlongPathArgs::new(&nodes[0], path, amt_msat, payment_hash, events.pop().unwrap())
+               .with_payment_secret(payment_secret)
+               .with_payment_metadata(payment_metadata.clone());
+       do_pass_along_path(args);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1]]], payment_preimage)
+       );
+
+       // Check that the payment parameter for max path length will prevent us from routing past our
+       // next-hop peer given the payment_metadata size.
+       let (mut route_0_2, payment_hash_2, payment_preimage_2, payment_secret_2) = get_route_and_payment_hash!(&nodes[0], &nodes[2], amt_msat);
+       let mut route_params_0_2 = route_0_2.route_params.clone().unwrap();
+       route_params_0_2.payment_params.max_path_length = 1;
+       nodes[0].router.expect_find_route_query(route_params_0_2);
+       let err = nodes[0].node.send_payment(payment_hash_2, recipient_onion_max_md_size.clone(), PaymentId(payment_hash_2.0), route_0_2.route_params.clone().unwrap(), Retry::Attempts(0)).unwrap_err();
+       assert_eq!(err, RetryableSendFailure::RouteNotFound);
+
+       // If our payment_metadata contains 1 additional byte, we'll fail prior to pathfinding.
+       let mut recipient_onion_too_large_md = recipient_onion_max_md_size.clone();
+       recipient_onion_too_large_md.payment_metadata.as_mut().map(|mut md| md.push(42));
+       let err = nodes[0].node.send_payment(payment_hash, recipient_onion_too_large_md.clone(), PaymentId(payment_hash.0), route_0_1.route_params.clone().unwrap(), Retry::Attempts(0)).unwrap_err();
+       assert_eq!(err, RetryableSendFailure::OnionPacketSizeExceeded);
+
+       // Confirm that we'll fail to construct an onion packet given this payment_metadata that's too
+       // large for even a 1-hop path.
+       let secp_ctx = Secp256k1::signing_only();
+       route_0_1.paths[0].hops[0].fee_msat = MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY;
+       route_0_1.paths[0].hops[0].cltv_expiry_delta = DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA;
+       let err = onion_utils::create_payment_onion(&secp_ctx, &route_0_1.paths[0], &test_utils::privkey(42), MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &recipient_onion_too_large_md, nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &payment_hash, &None, [0; 32]).unwrap_err();
+       match err {
+               APIError::InvalidRoute { err } => {
+                       assert_eq!(err, "Route size too large considering onion data");
+               },
+               _ => panic!(),
+       }
+
+       // If we remove enough payment_metadata bytes to allow for 2 hops, we're now able to send to
+       // nodes[2].
+       let mut recipient_onion_allows_2_hops = RecipientOnionFields {
+               payment_secret: Some(payment_secret_2),
+               payment_metadata: Some(vec![42; max_metadata_len - INTERMED_PAYLOAD_LEN_ESTIMATE]),
+               custom_tlvs: Vec::new(),
+       };
+       let mut route_params_0_2 = route_0_2.route_params.clone().unwrap();
+       route_params_0_2.payment_params.max_path_length = 2;
+       nodes[0].router.expect_find_route_query(route_params_0_2);
+       nodes[0].node.send_payment(payment_hash_2, recipient_onion_allows_2_hops.clone(), PaymentId(payment_hash_2.0), route_0_2.route_params.unwrap(), Retry::Attempts(0)).unwrap();
+       check_added_monitors!(nodes[0], 1);
+       let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+       assert_eq!(events.len(), 1);
+       let path = &[&nodes[1], &nodes[2]];
+       let args = PassAlongPathArgs::new(&nodes[0], path, amt_msat, payment_hash_2, events.pop().unwrap())
+               .with_payment_secret(payment_secret_2)
+               .with_payment_metadata(recipient_onion_allows_2_hops.payment_metadata.unwrap());
+       do_pass_along_path(args);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage_2)
+       );
+}
+
+#[test]
+fn one_hop_blinded_path_with_custom_tlv() {
+       // Test that we'll limit our maximum path length when paying to a 1-hop blinded path based on the
+       // size of the provided custom TLV, and refuse to send at all prior to pathfinding if it's too
+       // large.
+       let chanmon_cfgs = create_chanmon_cfgs(3);
+       let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+       let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+       create_announced_chan_between_nodes(&nodes, 0, 1);
+       let chan_upd_1_2 = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0).0.contents;
+
+       // Construct the route parameters for sending to nodes[2]'s 1-hop blinded path.
+       let amt_msat = 100_000;
+       let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), None);
+       let payee_tlvs = ReceiveTlvs {
+               payment_secret,
+               payment_constraints: PaymentConstraints {
+                       max_cltv_expiry: u32::max_value(),
+                       htlc_minimum_msat: chan_upd_1_2.htlc_minimum_msat,
+               },
+               payment_context: PaymentContext::unknown(),
+       };
+       let mut secp_ctx = Secp256k1::new();
+       let blinded_path = BlindedPath::one_hop_for_payment(
+               nodes[2].node.get_our_node_id(), payee_tlvs, TEST_FINAL_CLTV as u16,
+               &chanmon_cfgs[2].keys_manager, &secp_ctx
+       ).unwrap();
+       let route_params = RouteParameters::from_payment_params_and_value(
+               PaymentParameters::blinded(vec![blinded_path.clone()]),
+               amt_msat,
+       );
+
+       // Calculate the maximum custom TLV value size where a valid onion packet is still possible.
+       const CUSTOM_TLV_TYPE: u64 = 65537;
+       let final_payload_len_without_custom_tlv = msgs::OutboundOnionPayload::BlindedReceive {
+               sender_intended_htlc_amt_msat: MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY,
+               total_msat: MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY,
+               cltv_expiry_height: nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
+               encrypted_tlvs: &blinded_path.1.blinded_hops[0].encrypted_payload,
+               intro_node_blinding_point: Some(blinded_path.1.blinding_point),
+               keysend_preimage: None,
+               custom_tlvs: &Vec::new()
+       }.serialized_length();
+       let max_custom_tlv_len = 1300
+               - crate::util::ser::BigSize(CUSTOM_TLV_TYPE).serialized_length() // custom TLV type
+               - crate::util::ser::BigSize(1200).serialized_length() // custom TLV length
+               - 1 // onion payload varint prefix increased ser size due to custom TLV
+               - PAYLOAD_HMAC_LEN
+               - final_payload_len_without_custom_tlv;
+
+       // Check that we can send the maximum custom TLV with 1 blinded hop.
+       let recipient_onion_max_custom_tlv_size = RecipientOnionFields::spontaneous_empty()
+               .with_custom_tlvs(vec![(CUSTOM_TLV_TYPE, vec![42; max_custom_tlv_len])])
+               .unwrap();
+       nodes[1].node.send_payment(payment_hash, recipient_onion_max_custom_tlv_size.clone(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap();
+       check_added_monitors(&nodes[1], 1);
+
+       let mut events = nodes[1].node.get_and_clear_pending_msg_events();
+       assert_eq!(events.len(), 1);
+       let path = &[&nodes[2]];
+       let args = PassAlongPathArgs::new(&nodes[1], path, amt_msat, payment_hash, events.pop().unwrap())
+               .with_payment_secret(payment_secret)
+               .with_custom_tlvs(recipient_onion_max_custom_tlv_size.custom_tlvs.clone());
+       do_pass_along_path(args);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[1], &[&[&nodes[2]]], payment_preimage)
+                       .with_custom_tlvs(recipient_onion_max_custom_tlv_size.custom_tlvs.clone())
+       );
+
+       // If 1 byte is added to the custom TLV value, we'll fail to send prior to pathfinding.
+       let mut recipient_onion_too_large_custom_tlv = recipient_onion_max_custom_tlv_size.clone();
+       recipient_onion_too_large_custom_tlv.custom_tlvs[0].1.push(42);
+       let err = nodes[1].node.send_payment(payment_hash, recipient_onion_too_large_custom_tlv, PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap_err();
+       assert_eq!(err, RetryableSendFailure::OnionPacketSizeExceeded);
+
+       // With the maximum-size custom TLV, our max path length is limited to 1, so attempting to route
+       // nodes[0] -> nodes[2] will fail.
+       let err = nodes[0].node.send_payment(payment_hash, recipient_onion_max_custom_tlv_size.clone(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap_err();
+       assert_eq!(err, RetryableSendFailure::RouteNotFound);
+
+       // If we remove enough custom TLV bytes to allow for 1 intermediate unblinded hop, we're now able
+       // to send nodes[0] -> nodes[2].
+       let mut recipient_onion_allows_2_hops = recipient_onion_max_custom_tlv_size.clone();
+       recipient_onion_allows_2_hops.custom_tlvs[0].1.resize(max_custom_tlv_len - INTERMED_PAYLOAD_LEN_ESTIMATE, 0);
+       nodes[0].node.send_payment(payment_hash, recipient_onion_allows_2_hops.clone(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap();
+       check_added_monitors(&nodes[0], 1);
+
+       let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+       assert_eq!(events.len(), 1);
+       let path = &[&nodes[1], &nodes[2]];
+       let args = PassAlongPathArgs::new(&nodes[0], path, amt_msat, payment_hash, events.pop().unwrap())
+               .with_payment_secret(payment_secret)
+               .with_custom_tlvs(recipient_onion_allows_2_hops.custom_tlvs.clone());
+       do_pass_along_path(args);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage)
+                       .with_custom_tlvs(recipient_onion_allows_2_hops.custom_tlvs)
+       );
+}
+
+#[test]
+fn blinded_path_with_custom_tlv() {
+       // Test that we'll limit our maximum path length when paying to a blinded path based on the size
+       // of the provided custom TLV, and refuse to send at all prior to pathfinding if it's too large.
+       let chanmon_cfgs = create_chanmon_cfgs(4);
+       let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
+       let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
+       create_announced_chan_between_nodes(&nodes, 0, 1);
+       create_announced_chan_between_nodes(&nodes, 1, 2);
+       let chan_upd_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0).0.contents;
+
+       // Construct the route parameters for sending to nodes[3]'s blinded path.
+       let amt_msat = 100_000;
+       let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[3], Some(amt_msat), None);
+       let route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
+               nodes.iter().skip(2).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_2_3],
+               &chanmon_cfgs[3].keys_manager);
+
+       // Calculate the maximum custom TLV value size where a valid onion packet is still possible.
+       const CUSTOM_TLV_TYPE: u64 = 65537;
+       let mut route = get_route(&nodes[1], &route_params).unwrap();
+       let reserved_packet_bytes_without_custom_tlv: usize = onion_utils::build_onion_payloads(
+               &route.paths[0], MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY,
+               &RecipientOnionFields::spontaneous_empty(),
+               nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &None
+       )
+               .unwrap()
+               .0
+               .iter()
+               .map(|payload| payload.serialized_length() + PAYLOAD_HMAC_LEN)
+               .sum();
+       let max_custom_tlv_len = 1300
+               - crate::util::ser::BigSize(CUSTOM_TLV_TYPE).serialized_length() // custom TLV type
+               - crate::util::ser::BigSize(1200).serialized_length() // custom TLV length
+               - 2 // onion payload varint prefix increased ser size due to custom TLV
+               - reserved_packet_bytes_without_custom_tlv;
+
+       // Check that we can send the maximum custom TLV size with 0 intermediate unblinded hops.
+       let recipient_onion_max_custom_tlv_size = RecipientOnionFields::spontaneous_empty()
+               .with_custom_tlvs(vec![(CUSTOM_TLV_TYPE, vec![42; max_custom_tlv_len])])
+               .unwrap();
+       nodes[1].node.send_payment(payment_hash, recipient_onion_max_custom_tlv_size.clone(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap();
+       check_added_monitors(&nodes[1], 1);
+
+       let mut events = nodes[1].node.get_and_clear_pending_msg_events();
+       assert_eq!(events.len(), 1);
+       let path = &[&nodes[2], &nodes[3]];
+       let args = PassAlongPathArgs::new(&nodes[1], path, amt_msat, payment_hash, events.pop().unwrap())
+               .with_payment_secret(payment_secret)
+               .with_custom_tlvs(recipient_onion_max_custom_tlv_size.custom_tlvs.clone());
+       do_pass_along_path(args);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[1], &[&[&nodes[2], &nodes[3]]], payment_preimage)
+                       .with_custom_tlvs(recipient_onion_max_custom_tlv_size.custom_tlvs.clone())
+       );
+
+       // If 1 byte is added to the custom TLV value, we'll fail to send prior to pathfinding.
+       let mut recipient_onion_too_large_custom_tlv = recipient_onion_max_custom_tlv_size.clone();
+       recipient_onion_too_large_custom_tlv.custom_tlvs[0].1.push(42);
+       let err = nodes[1].node.send_payment(payment_hash, recipient_onion_too_large_custom_tlv.clone(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap_err();
+       assert_eq!(err, RetryableSendFailure::OnionPacketSizeExceeded);
+
+       // Confirm that we can't construct an onion packet given this too-large custom TLV.
+       let secp_ctx = Secp256k1::signing_only();
+       route.paths[0].hops[0].fee_msat = MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY;
+       route.paths[0].hops[0].cltv_expiry_delta = DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA;
+       let err = onion_utils::create_payment_onion(&secp_ctx, &route.paths[0], &test_utils::privkey(42), MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &recipient_onion_too_large_custom_tlv, nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &payment_hash, &None, [0; 32]).unwrap_err();
+       match err {
+               APIError::InvalidRoute { err } => {
+                       assert_eq!(err, "Route size too large considering onion data");
+               },
+               _ => panic!(),
+       }
+
+       // With the maximum-size custom TLV, we can't have any intermediate unblinded hops, so attempting
+       // to route nodes[0] -> nodes[3] will fail.
+       let err = nodes[0].node.send_payment(payment_hash, recipient_onion_max_custom_tlv_size.clone(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap_err();
+       assert_eq!(err, RetryableSendFailure::RouteNotFound);
+
+       // If we remove enough custom TLV bytes to allow for 1 intermediate unblinded hop, we're now able
+       // to send nodes[0] -> nodes[3].
+       let mut recipient_onion_allows_2_hops = recipient_onion_max_custom_tlv_size.clone();
+       recipient_onion_allows_2_hops.custom_tlvs[0].1.resize(max_custom_tlv_len - INTERMED_PAYLOAD_LEN_ESTIMATE, 0);
+       nodes[0].node.send_payment(payment_hash, recipient_onion_allows_2_hops.clone(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap();
+       check_added_monitors(&nodes[0], 1);
+
+       let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+       assert_eq!(events.len(), 1);
+       let path = &[&nodes[1], &nodes[2], &nodes[3]];
+       let args = PassAlongPathArgs::new(&nodes[0], path, amt_msat, payment_hash, events.pop().unwrap())
+               .with_payment_secret(payment_secret)
+               .with_custom_tlvs(recipient_onion_allows_2_hops.custom_tlvs.clone());
+       do_pass_along_path(args);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3]]], payment_preimage)
+                       .with_custom_tlvs(recipient_onion_allows_2_hops.custom_tlvs)
+       );
+}
index 71b73390d1c80a9590afedecfbee6f542822a076..5e2ae44c99810a5ddece06f6b5e028618d5d24c2 100644 (file)
@@ -22,7 +22,9 @@ pub mod peer_handler;
 pub mod chan_utils;
 pub mod features;
 pub mod script;
-mod channel_id;
+pub mod types;
+
+pub use types::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
 
 #[cfg(fuzzing)]
 pub mod peer_channel_encryptor;
@@ -34,9 +36,6 @@ pub mod channel;
 #[cfg(not(fuzzing))]
 pub(crate) mod channel;
 
-// Re-export ChannelId
-pub use channel_id::ChannelId;
-
 pub(crate) mod onion_utils;
 mod outbound_payment;
 pub mod wire;
@@ -54,6 +53,9 @@ mod blinded_payment_tests;
 mod functional_tests;
 #[cfg(test)]
 #[allow(unused_mut)]
+mod max_payment_path_len_tests;
+#[cfg(test)]
+#[allow(unused_mut)]
 mod payment_tests;
 #[cfg(test)]
 #[allow(unused_mut)]
@@ -82,75 +84,7 @@ mod async_signer_tests;
 #[cfg(test)]
 #[allow(unused_mut)]
 mod offers_tests;
+#[allow(dead_code)] // TODO(dual_funding): Exchange for dual_funding cfg
+pub(crate) mod interactivetxs;
 
 pub use self::peer_channel_encryptor::LN_MAX_MSG_LEN;
-
-use bitcoin::hashes::{sha256::Hash as Sha256, Hash};
-
-/// 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, Ord, PartialOrd)]
-pub struct PaymentHash(pub [u8; 32]);
-
-impl core::fmt::Display for PaymentHash {
-       fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
-               crate::util::logger::DebugBytes(&self.0).fmt(f)
-       }
-}
-
-/// 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, Ord, PartialOrd)]
-pub struct PaymentPreimage(pub [u8; 32]);
-
-impl core::fmt::Display for PaymentPreimage {
-       fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
-               crate::util::logger::DebugBytes(&self.0).fmt(f)
-       }
-}
-
-/// Converts a `PaymentPreimage` into a `PaymentHash` by hashing the preimage with SHA256.
-impl From<PaymentPreimage> for PaymentHash {
-       fn from(value: PaymentPreimage) -> Self {
-               PaymentHash(Sha256::hash(&value.0).to_byte_array())
-       }
-}
-
-/// 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, Ord, PartialOrd)]
-pub struct PaymentSecret(pub [u8; 32]);
-
-use crate::prelude::*;
-use bitcoin::bech32;
-use bitcoin::bech32::{Base32Len, FromBase32, ToBase32, WriteBase32, u5};
-
-impl FromBase32 for PaymentSecret {
-       type Err = bech32::Error;
-
-       fn from_base32(field_data: &[u5]) -> Result<PaymentSecret, bech32::Error> {
-               if field_data.len() != 52 {
-                       return Err(bech32::Error::InvalidLength)
-               } else {
-                       let data_bytes = Vec::<u8>::from_base32(field_data)?;
-                       let mut payment_secret = [0; 32];
-                       payment_secret.copy_from_slice(&data_bytes);
-                       Ok(PaymentSecret(payment_secret))
-               }
-       }
-}
-
-impl ToBase32 for PaymentSecret {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               (&self.0[..]).write_base32(writer)
-       }
-}
-
-impl Base32Len for PaymentSecret {
-       fn base32_len(&self) -> usize {
-               52
-       }
-}
index 25084689fb73ff6c817fca706638a25689e84d7c..575d7d6b18e66d7a9aec6f47ec1b9eac2b372f2a 100644 (file)
@@ -9,16 +9,16 @@
 
 //! Further functional tests which test blockchain reorganizations.
 
-use crate::sign::{ecdsa::EcdsaChannelSigner, SpendableOutputDescriptor};
+use crate::sign::{ecdsa::EcdsaChannelSigner, OutputSpender, SpendableOutputDescriptor};
 use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS, Balance};
 use crate::chain::transaction::OutPoint;
 use crate::chain::chaininterface::{LowerBoundedFeeEstimator, compute_feerate_sat_per_1000_weight};
 use crate::events::bump_transaction::{BumpTransactionEvent, WalletSource};
 use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, ClosureReason, HTLCDestination};
-use crate::ln::{channel, ChannelId};
+use crate::ln::channel;
+use crate::ln::types::ChannelId;
 use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, PaymentId, RecipientOnionFields};
 use crate::ln::msgs::ChannelMessageHandler;
-use crate::util::config::UserConfig;
 use crate::crypto::utils::sign;
 use crate::util::ser::Writeable;
 use crate::util::scid_utils::block_from_scid;
@@ -31,6 +31,7 @@ use bitcoin::blockdata::opcodes;
 use bitcoin::hashes::hex::FromHex;
 use bitcoin::secp256k1::{Secp256k1, SecretKey};
 use bitcoin::sighash::{SighashCache, EcdsaSighashType};
+use bitcoin::transaction::Version;
 
 use crate::prelude::*;
 
@@ -158,6 +159,60 @@ fn revoked_output_htlc_resolution_timing() {
        expect_payment_failed!(nodes[1], payment_hash_1, false);
 }
 
+#[test]
+fn archive_fully_resolved_monitors() {
+       // Test we can archive fully resolved channel monitor.
+       let chanmon_cfgs = create_chanmon_cfgs(2);
+       let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+       let mut user_config = test_default_channel_config();
+       let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(user_config), Some(user_config)]);
+       let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+       let (_, _, chan_id, funding_tx) =
+               create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 1_000_000);
+
+       nodes[0].node.close_channel(&chan_id, &nodes[1].node.get_our_node_id()).unwrap();
+       let node_0_shutdown = get_event_msg!(nodes[0], MessageSendEvent::SendShutdown, nodes[1].node.get_our_node_id());
+       nodes[1].node.handle_shutdown(&nodes[0].node.get_our_node_id(), &node_0_shutdown);
+       let node_1_shutdown = get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id());
+       nodes[0].node.handle_shutdown(&nodes[1].node.get_our_node_id(), &node_1_shutdown);
+
+       let node_0_closing_signed = get_event_msg!(nodes[0], MessageSendEvent::SendClosingSigned, nodes[1].node.get_our_node_id());
+       nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &node_0_closing_signed);
+       let node_1_closing_signed = get_event_msg!(nodes[1], MessageSendEvent::SendClosingSigned, nodes[0].node.get_our_node_id());
+       nodes[0].node.handle_closing_signed(&nodes[1].node.get_our_node_id(), &node_1_closing_signed);
+       let (_, node_0_2nd_closing_signed) = get_closing_signed_broadcast!(nodes[0].node, nodes[1].node.get_our_node_id());
+       nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &node_0_2nd_closing_signed.unwrap());
+       let (_, _) = get_closing_signed_broadcast!(nodes[1].node, nodes[0].node.get_our_node_id());
+
+       let shutdown_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
+
+       mine_transaction(&nodes[0], &shutdown_tx[0]);
+       mine_transaction(&nodes[1], &shutdown_tx[0]);
+
+       connect_blocks(&nodes[0], 6);
+       connect_blocks(&nodes[1], 6);
+
+       check_closed_event!(nodes[0], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 1000000);
+       check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[0].node.get_our_node_id()], 1000000);
+
+       assert_eq!(nodes[0].chain_monitor.chain_monitor.list_monitors().len(), 1);
+       // First archive should set balances_empty_height to current block height
+       nodes[0].chain_monitor.chain_monitor.archive_fully_resolved_channel_monitors(); 
+       assert_eq!(nodes[0].chain_monitor.chain_monitor.list_monitors().len(), 1);
+       connect_blocks(&nodes[0], 4032);
+       // Second call after 4032 blocks, should archive the monitor
+       nodes[0].chain_monitor.chain_monitor.archive_fully_resolved_channel_monitors();
+       // Should have no monitors left
+       assert_eq!(nodes[0].chain_monitor.chain_monitor.list_monitors().len(), 0);
+       // Remove the corresponding outputs and transactions the chain source is
+       // watching. This is to make sure the `Drop` function assertions pass.
+       nodes.get_mut(0).unwrap().chain_source.remove_watched_txn_and_outputs(
+               OutPoint { txid: funding_tx.txid(), index: 0 },
+               funding_tx.output[0].script_pubkey.clone()
+       );
+}
+
 fn do_chanmon_claim_value_coop_close(anchors: bool) {
        // Tests `get_claimable_balances` returns the correct values across a simple cooperative claim.
        // Specifically, this tests that the channel non-HTLC balances show up in
@@ -305,16 +360,16 @@ fn do_test_claim_value_force_close(anchors: bool, prev_commitment_tx: bool) {
        let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
 
        let coinbase_tx = Transaction {
-               version: 2,
+               version: Version::TWO,
                lock_time: LockTime::ZERO,
                input: vec![TxIn { ..Default::default() }],
                output: vec![
                        TxOut {
-                               value: Amount::ONE_BTC.to_sat(),
+                               value: Amount::ONE_BTC,
                                script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
                        },
                        TxOut {
-                               value: Amount::ONE_BTC.to_sat(),
+                               value: Amount::ONE_BTC,
                                script_pubkey: nodes[1].wallet_source.get_change_script().unwrap(),
                        },
                ],
@@ -481,8 +536,8 @@ fn do_test_claim_value_force_close(anchors: bool, prev_commitment_tx: bool) {
        check_spends!(b_broadcast_txn[1], remote_txn[0], coinbase_tx);
        assert_eq!(b_broadcast_txn[0].input.len(), if anchors { 2 } else { 1 });
        assert_eq!(b_broadcast_txn[1].input.len(), if anchors { 2 } else { 1 });
-       assert_eq!(remote_txn[0].output[b_broadcast_txn[0].input[0].previous_output.vout as usize].value, 3_000);
-       assert_eq!(remote_txn[0].output[b_broadcast_txn[1].input[0].previous_output.vout as usize].value, 4_000);
+       assert_eq!(remote_txn[0].output[b_broadcast_txn[0].input[0].previous_output.vout as usize].value.to_sat(), 3_000);
+       assert_eq!(remote_txn[0].output[b_broadcast_txn[1].input[0].previous_output.vout as usize].value.to_sat(), 4_000);
 
        assert!(nodes[0].node.list_channels().is_empty());
        check_closed_broadcast!(nodes[0], true);
@@ -561,8 +616,8 @@ fn do_test_claim_value_force_close(anchors: bool, prev_commitment_tx: bool) {
        assert_ne!(a_broadcast_txn[0].input[0].previous_output.vout,
                   a_broadcast_txn[1].input[0].previous_output.vout);
        // a_broadcast_txn [0] and [1] should spend the HTLC outputs of the commitment tx
-       assert_eq!(remote_txn[0].output[a_broadcast_txn[0].input[0].previous_output.vout as usize].value, 3_000);
-       assert_eq!(remote_txn[0].output[a_broadcast_txn[1].input[0].previous_output.vout as usize].value, 4_000);
+       assert_eq!(remote_txn[0].output[a_broadcast_txn[0].input[0].previous_output.vout as usize].value.to_sat(), 3_000);
+       assert_eq!(remote_txn[0].output[a_broadcast_txn[1].input[0].previous_output.vout as usize].value.to_sat(), 4_000);
 
        // Once the HTLC-Timeout transaction confirms, A will no longer consider the HTLC
        // "MaybeClaimable", but instead move it to "AwaitingConfirmations".
@@ -666,16 +721,16 @@ fn do_test_balances_on_local_commitment_htlcs(anchors: bool) {
        let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
 
        let coinbase_tx = Transaction {
-               version: 2,
+               version: Version::TWO,
                lock_time: LockTime::ZERO,
                input: vec![TxIn { ..Default::default() }],
                output: vec![
                        TxOut {
-                               value: Amount::ONE_BTC.to_sat(),
+                               value: Amount::ONE_BTC,
                                script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
                        },
                        TxOut {
-                               value: Amount::ONE_BTC.to_sat(),
+                               value: Amount::ONE_BTC,
                                script_pubkey: nodes[1].wallet_source.get_change_script().unwrap(),
                        },
                ],
@@ -1188,14 +1243,14 @@ fn do_test_revoked_counterparty_commitment_balances(anchors: bool, confirm_htlc_
        assert!(failed_payments.is_empty());
        if let Event::PendingHTLCsForwardable { .. } = events[0] {} else { panic!(); }
        match &events[1] {
-               Event::ChannelClosed { reason: ClosureReason::HolderForceClosed, .. } => {},
+               Event::ChannelClosed { reason: ClosureReason::HTLCsTimedOut, .. } => {},
                _ => panic!(),
        }
 
        connect_blocks(&nodes[1], htlc_cltv_timeout + 1 - 10);
        check_closed_broadcast!(nodes[1], true);
        check_added_monitors!(nodes[1], 1);
-       check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed, [nodes[0].node.get_our_node_id()], 1000000);
+       check_closed_event!(nodes[1], 1, ClosureReason::HTLCsTimedOut, [nodes[0].node.get_our_node_id()], 1000000);
 
        // Prior to channel closure, B considers the preimage HTLC as its own, and otherwise only
        // lists the two on-chain timeout-able HTLCs as claimable balances.
@@ -1221,7 +1276,7 @@ fn do_test_revoked_counterparty_commitment_balances(anchors: bool, confirm_htlc_
        // Currently the revoked commitment is claimed in four transactions as the HTLCs all expire
        // quite soon.
        assert_eq!(claim_txn.len(), 4);
-       claim_txn.sort_unstable_by_key(|tx| tx.output.iter().map(|output| output.value).sum::<u64>());
+       claim_txn.sort_unstable_by_key(|tx| tx.output.iter().map(|output| output.value.to_sat()).sum::<u64>());
 
        // The following constants were determined experimentally
        const BS_TO_SELF_CLAIM_EXP_WEIGHT: u64 = 483;
@@ -1380,16 +1435,16 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) {
        let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
 
        let coinbase_tx = Transaction {
-               version: 2,
+               version: Version::TWO,
                lock_time: LockTime::ZERO,
                input: vec![TxIn { ..Default::default() }],
                output: vec![
                        TxOut {
-                               value: Amount::ONE_BTC.to_sat(),
+                               value: Amount::ONE_BTC,
                                script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
                        },
                        TxOut {
-                               value: Amount::ONE_BTC.to_sat(),
+                               value: Amount::ONE_BTC,
                                script_pubkey: nodes[1].wallet_source.get_change_script().unwrap(),
                        },
                ],
@@ -1411,9 +1466,9 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) {
        assert_eq!(revoked_local_txn[0].input.len(), 1);
        assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, funding_tx.txid());
        if anchors {
-               assert_eq!(revoked_local_txn[0].output[4].value, 11000); // to_self output
+               assert_eq!(revoked_local_txn[0].output[4].value.to_sat(), 11000); // to_self output
        } else {
-               assert_eq!(revoked_local_txn[0].output[2].value, 11000); // to_self output
+               assert_eq!(revoked_local_txn[0].output[2].value.to_sat(), 11000); // to_self output
        }
 
        // The to-be-revoked commitment tx should have two HTLCs, an output for each side, and an
@@ -1535,10 +1590,10 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) {
        if anchors {
                // With anchors, B can pay for revoked_htlc_success's fee with additional inputs, rather
                // than with the HTLC itself.
-               fuzzy_assert_eq(as_htlc_claim_tx[0].output[0].value,
+               fuzzy_assert_eq(as_htlc_claim_tx[0].output[0].value.to_sat(),
                        3_000 - as_revoked_htlc_success_claim_fee);
        } else {
-               fuzzy_assert_eq(as_htlc_claim_tx[0].output[0].value,
+               fuzzy_assert_eq(as_htlc_claim_tx[0].output[0].value.to_sat(),
                        3_000 - revoked_htlc_success_fee - as_revoked_htlc_success_claim_fee);
        }
 
@@ -1553,7 +1608,7 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) {
                }, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
                        amount_satoshis: 1_000,
                }, Balance::ClaimableAwaitingConfirmations {
-                       amount_satoshis: as_htlc_claim_tx[0].output[0].value,
+                       amount_satoshis: as_htlc_claim_tx[0].output[0].value.to_sat(),
                        confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1,
                }]),
                sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
@@ -1566,7 +1621,7 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) {
                }, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
                        amount_satoshis: 1_000,
                }, Balance::ClaimableAwaitingConfirmations {
-                       amount_satoshis: as_htlc_claim_tx[0].output[0].value,
+                       amount_satoshis: as_htlc_claim_tx[0].output[0].value.to_sat(),
                        confirmation_height: nodes[0].best_block_info().1 + 2,
                }]),
                sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
@@ -1635,7 +1690,7 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) {
                        // to_self output in B's revoked commitment
                        amount_satoshis: 11_000,
                }, Balance::ClaimableAwaitingConfirmations {
-                       amount_satoshis: revoked_htlc_timeout_claim.output[0].value,
+                       amount_satoshis: revoked_htlc_timeout_claim.output[0].value.to_sat(),
                        confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1,
                }]),
                sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
@@ -1643,10 +1698,10 @@ fn do_test_revoked_counterparty_htlc_tx_balances(anchors: bool) {
        mine_transaction(&nodes[0], &revoked_to_self_claim);
        assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
                        // to_self output in B's revoked commitment
-                       amount_satoshis: revoked_to_self_claim.output[0].value,
+                       amount_satoshis: revoked_to_self_claim.output[0].value.to_sat(),
                        confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1,
                }, Balance::ClaimableAwaitingConfirmations {
-                       amount_satoshis: revoked_htlc_timeout_claim.output[0].value,
+                       amount_satoshis: revoked_htlc_timeout_claim.output[0].value.to_sat(),
                        confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 2,
                }]),
                sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
@@ -1692,11 +1747,11 @@ fn do_test_revoked_counterparty_aggregated_claims(anchors: bool) {
        let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
 
        let coinbase_tx = Transaction {
-               version: 2,
+               version: Version::TWO,
                lock_time: LockTime::ZERO,
                input: vec![TxIn { ..Default::default() }],
                output: vec![TxOut {
-                       value: Amount::ONE_BTC.to_sat(),
+                       value: Amount::ONE_BTC,
                        script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
                }],
        };
@@ -1889,7 +1944,7 @@ fn do_test_revoked_counterparty_aggregated_claims(anchors: bool) {
                }, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1
                        amount_satoshis: 4_000,
                }, Balance::ClaimableAwaitingConfirmations { // HTLC 2
-                       amount_satoshis: claim_txn_2[0].output[0].value,
+                       amount_satoshis: claim_txn_2[0].output[0].value.to_sat(),
                        confirmation_height: htlc_2_claim_maturity,
                }]),
                sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
@@ -1914,16 +1969,16 @@ fn do_test_revoked_counterparty_aggregated_claims(anchors: bool) {
 
        if anchors {
                assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
-                               amount_satoshis: claim_txn_2[1].output[0].value,
+                               amount_satoshis: claim_txn_2[1].output[0].value.to_sat(),
                                confirmation_height: rest_claim_maturity,
                        }, Balance::ClaimableAwaitingConfirmations {
-                               amount_satoshis: revoked_to_self_claim.as_ref().unwrap().output[0].value,
+                               amount_satoshis: revoked_to_self_claim.as_ref().unwrap().output[0].value.to_sat(),
                                confirmation_height: rest_claim_maturity,
                        }],
                        nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
        } else {
                assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
-                               amount_satoshis: claim_txn_2[1].output[0].value,
+                               amount_satoshis: claim_txn_2[1].output[0].value.to_sat(),
                                confirmation_height: rest_claim_maturity,
                        }],
                        nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
@@ -2081,11 +2136,11 @@ fn do_test_monitor_rebroadcast_pending_claims(anchors: bool) {
        check_added_monitors(&nodes[0], 1);
 
        let coinbase_tx = Transaction {
-               version: 2,
+               version: Version::TWO,
                lock_time: LockTime::ZERO,
                input: vec![TxIn { ..Default::default() }],
                output: vec![TxOut { // UTXO to attach fees to `htlc_tx` on anchors
-                       value: Amount::ONE_BTC.to_sat(),
+                       value: Amount::ONE_BTC,
                        script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
                }],
        };
@@ -2110,8 +2165,8 @@ fn do_test_monitor_rebroadcast_pending_claims(anchors: bool) {
                                        assert_eq!(txn.len(), 1);
                                        let htlc_tx = txn.pop().unwrap();
                                        check_spends!(&htlc_tx, &commitment_txn[0], &coinbase_tx);
-                                       let htlc_tx_fee = HTLC_AMT_SAT + coinbase_tx.output[0].value -
-                                               htlc_tx.output.iter().map(|output| output.value).sum::<u64>();
+                                       let htlc_tx_fee = HTLC_AMT_SAT + coinbase_tx.output[0].value.to_sat() -
+                                               htlc_tx.output.iter().map(|output| output.value.to_sat()).sum::<u64>();
                                        let htlc_tx_weight = htlc_tx.weight().to_wu();
                                        (htlc_tx, compute_feerate_sat_per_1000_weight(htlc_tx_fee, htlc_tx_weight))
                                }
@@ -2126,7 +2181,7 @@ fn do_test_monitor_rebroadcast_pending_claims(anchors: bool) {
                        }
                        let htlc_tx = txn.pop().unwrap();
                        check_spends!(htlc_tx, commitment_txn[0]);
-                       let htlc_tx_fee = HTLC_AMT_SAT - htlc_tx.output[0].value;
+                       let htlc_tx_fee = HTLC_AMT_SAT - htlc_tx.output[0].value.to_sat();
                        let htlc_tx_weight = htlc_tx.weight().to_wu();
                        (htlc_tx, compute_feerate_sat_per_1000_weight(htlc_tx_fee, htlc_tx_weight))
                };
@@ -2195,7 +2250,7 @@ fn test_yield_anchors_events() {
        // emitted by LDK, such that the consumer can attach fees to the zero fee HTLC transactions.
        let mut chanmon_cfgs = create_chanmon_cfgs(2);
        let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
-       let mut anchors_config = UserConfig::default();
+       let mut anchors_config = test_default_channel_config();
        anchors_config.channel_handshake_config.announced_channel = true;
        anchors_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
        anchors_config.manually_accept_inbound_channels = true;
@@ -2237,11 +2292,11 @@ fn test_yield_anchors_events() {
        let (commitment_tx, anchor_tx) = match holder_events.pop().unwrap() {
                Event::BumpTransaction(event) => {
                        let coinbase_tx = Transaction {
-                               version: 2,
+                               version: Version::TWO,
                                lock_time: LockTime::ZERO,
                                input: vec![TxIn { ..Default::default() }],
                                output: vec![TxOut { // UTXO to attach fees to `anchor_tx`
-                                       value: Amount::ONE_BTC.to_sat(),
+                                       value: Amount::ONE_BTC,
                                        script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
                                }],
                        };
@@ -2258,8 +2313,8 @@ fn test_yield_anchors_events() {
                _ => panic!("Unexpected event"),
        };
 
-       assert_eq!(commitment_tx.output[2].value, 1_000); // HTLC A -> B
-       assert_eq!(commitment_tx.output[3].value, 2_000); // HTLC B -> A
+       assert_eq!(commitment_tx.output[2].value.to_sat(), 1_000); // HTLC A -> B
+       assert_eq!(commitment_tx.output[3].value.to_sat(), 2_000); // HTLC B -> A
 
        mine_transactions(&nodes[0], &[&commitment_tx, &anchor_tx]);
        check_added_monitors!(nodes[0], 1);
@@ -2346,7 +2401,7 @@ fn test_anchors_aggregated_revoked_htlc_tx() {
        let bob_persister;
        let bob_chain_monitor;
 
-       let mut anchors_config = UserConfig::default();
+       let mut anchors_config = test_default_channel_config();
        anchors_config.channel_handshake_config.announced_channel = true;
        anchors_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
        anchors_config.manually_accept_inbound_channels = true;
@@ -2414,9 +2469,9 @@ fn test_anchors_aggregated_revoked_htlc_tx() {
        let mut revoked_commitment_txs = Vec::with_capacity(events.len());
        let mut anchor_txs = Vec::with_capacity(events.len());
        for (idx, event) in events.into_iter().enumerate() {
-               let utxo_value = Amount::ONE_BTC.to_sat() * (idx + 1) as u64;
+               let utxo_value = Amount::ONE_BTC * (idx + 1) as u64;
                let coinbase_tx = Transaction {
-                       version: 2,
+                       version: Version::TWO,
                        lock_time: LockTime::ZERO,
                        input: vec![TxIn { ..Default::default() }],
                        output: vec![TxOut { // UTXO to attach fees to `anchor_tx`
@@ -2491,18 +2546,18 @@ fn test_anchors_aggregated_revoked_htlc_tx() {
        let htlc_tx = {
                let secret_key = SecretKey::from_slice(&[1; 32]).unwrap();
                let public_key = PublicKey::new(secret_key.public_key(&secp));
-               let fee_utxo_script = ScriptBuf::new_v0_p2wpkh(&public_key.wpubkey_hash().unwrap());
+               let fee_utxo_script = ScriptBuf::new_p2wpkh(&public_key.wpubkey_hash().unwrap());
                let coinbase_tx = Transaction {
-                       version: 2,
+                       version: Version::TWO,
                        lock_time: LockTime::ZERO,
                        input: vec![TxIn { ..Default::default() }],
                        output: vec![TxOut { // UTXO to attach fees to `htlc_tx`
-                               value: Amount::ONE_BTC.to_sat(),
+                               value: Amount::ONE_BTC,
                                script_pubkey: fee_utxo_script.clone(),
                        }],
                };
                let mut htlc_tx = Transaction {
-                       version: 2,
+                       version: Version::TWO,
                        lock_time: LockTime::ZERO,
                        input: vec![TxIn { // Fee input
                                previous_output: bitcoin::OutPoint { txid: coinbase_tx.txid(), vout: 0 },
@@ -2539,7 +2594,7 @@ fn test_anchors_aggregated_revoked_htlc_tx() {
                }
                let fee_utxo_sig = {
                        let witness_script = ScriptBuf::new_p2pkh(&public_key.pubkey_hash());
-                       let sighash = hash_to_message!(&SighashCache::new(&htlc_tx).segwit_signature_hash(
+                       let sighash = hash_to_message!(&SighashCache::new(&htlc_tx).p2wsh_signature_hash(
                                0, &witness_script, coinbase_tx.output[0].value, EcdsaSighashType::All
                        ).unwrap()[..]);
                        let sig = sign(&secp, &sighash, &secret_key);
@@ -2649,7 +2704,7 @@ fn do_test_anchors_monitor_fixes_counterparty_payment_script_on_reload(confirm_c
        let secp = Secp256k1::new();
        let privkey = bitcoin::PrivateKey::from_slice(&[1; 32], bitcoin::Network::Testnet).unwrap();
        let pubkey = bitcoin::PublicKey::from_private_key(&secp, &privkey);
-       let p2wpkh_script = ScriptBuf::new_v0_p2wpkh(&pubkey.wpubkey_hash().unwrap());
+       let p2wpkh_script = ScriptBuf::new_p2wpkh(&pubkey.wpubkey_hash().unwrap());
        get_monitor!(nodes[1], chan_id).set_counterparty_payment_script(p2wpkh_script.clone());
        assert_eq!(get_monitor!(nodes[1], chan_id).get_counterparty_payment_script(), p2wpkh_script);
 
@@ -2688,7 +2743,7 @@ fn do_test_anchors_monitor_fixes_counterparty_payment_script_on_reload(confirm_c
        };
        check_closed_event!(&nodes[1], 1, ClosureReason::CommitmentTxConfirmed, false,
                 [nodes[0].node.get_our_node_id()], 100000);
-       assert!(get_monitor!(nodes[1], chan_id).get_counterparty_payment_script().is_v0_p2wsh());
+       assert!(get_monitor!(nodes[1], chan_id).get_counterparty_payment_script().is_p2wsh());
 
        connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
        connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
@@ -2731,12 +2786,12 @@ fn do_test_monitor_claims_with_random_signatures(anchors: bool, confirm_counterp
        let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
 
        let coinbase_tx = Transaction {
-               version: 2,
+               version: Version::TWO,
                lock_time: LockTime::ZERO,
                input: vec![TxIn { ..Default::default() }],
                output: vec![
                        TxOut {
-                               value: Amount::ONE_BTC.to_sat(),
+                               value: Amount::ONE_BTC,
                                script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
                        },
                ],
@@ -2823,3 +2878,42 @@ fn test_monitor_claims_with_random_signatures() {
        do_test_monitor_claims_with_random_signatures(true, false);
        do_test_monitor_claims_with_random_signatures(true, true);
 }
+
+#[test]
+fn test_event_replay_causing_monitor_replay() {
+       // In LDK 0.0.121 there was a bug where if a `PaymentSent` event caused an RAA
+       // `ChannelMonitorUpdate` hold and then the node was restarted after the `PaymentSent` event
+       // and `ChannelMonitorUpdate` both completed but without persisting the `ChannelManager` we'd
+       // replay the `ChannelMonitorUpdate` on restart (which is fine, but triggered a safety panic).
+       let chanmon_cfgs = create_chanmon_cfgs(2);
+       let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+       let persister;
+       let new_chain_monitor;
+       let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+       let node_deserialized;
+       let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+       let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 500_000_000);
+
+       let payment_preimage = route_payment(&nodes[0], &[&nodes[1]], 1_000_000).0;
+
+       do_claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1]]], payment_preimage)
+       );
+
+       // At this point the `PaymentSent` event has not been processed but the full commitment signed
+       // dance has completed.
+       let serialized_channel_manager = nodes[0].node.encode();
+
+       // Now process the `PaymentSent` to get the final RAA `ChannelMonitorUpdate`, checking that it
+       // resulted in a `ChannelManager` persistence request.
+       nodes[0].node.get_and_clear_needs_persistence();
+       expect_payment_sent(&nodes[0], payment_preimage, None, true, true /* expected post-event monitor update*/);
+       assert!(nodes[0].node.get_and_clear_needs_persistence());
+
+       let serialized_monitor = get_monitor!(nodes[0], chan.2).encode();
+       reload_node!(nodes[0], &serialized_channel_manager, &[&serialized_monitor], persister, new_chain_monitor, node_deserialized);
+
+       // Expect the `PaymentSent` to get replayed, this time without the duplicate monitor update
+       expect_payment_sent(&nodes[0], payment_preimage, None, false, false /* expected post-event monitor update*/);
+}
index d291ac8664aceec5fdaff3b0d2ce20077e727028..3b52ff0def9881b0af7626615d19255b11266db1 100644 (file)
@@ -32,15 +32,15 @@ use bitcoin::blockdata::script::ScriptBuf;
 use bitcoin::hash_types::Txid;
 
 use crate::blinded_path::payment::{BlindedPaymentTlvs, ForwardTlvs, ReceiveTlvs};
-use crate::ln::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
+use crate::ln::types::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
 use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
 use crate::ln::onion_utils;
 use crate::onion_message;
 use crate::sign::{NodeSigner, Recipient};
 
+#[allow(unused_imports)]
 use crate::prelude::*;
-#[cfg(feature = "std")]
-use core::convert::TryFrom;
+
 use core::fmt;
 use core::fmt::Debug;
 use core::ops::Deref;
@@ -55,7 +55,7 @@ use crate::io_extras::read_to_end;
 use crate::events::{EventsProvider, MessageSendEventsProvider};
 use crate::crypto::streams::ChaChaPolyReadAdapter;
 use crate::util::logger;
-use crate::util::ser::{LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize};
+use crate::util::ser::{BigSize, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, LengthRead, LengthReadable, LengthReadableArgs, Readable, ReadableArgs, TransactionU16LenLimited, WithoutLength, Writeable, Writer};
 use crate::util::base32;
 
 use crate::routing::gossip::{NodeAlias, NodeId};
@@ -91,6 +91,16 @@ pub enum DecodeError {
        Io(io::ErrorKind),
        /// The message included zlib-compressed values, which we don't support.
        UnsupportedCompression,
+       /// Value is validly encoded but is dangerous to use.
+       ///
+       /// This is used for things like [`ChannelManager`] deserialization where we want to ensure
+       /// that we don't use a [`ChannelManager`] which is in out of sync with the [`ChannelMonitor`].
+       /// This indicates that there is a critical implementation flaw in the storage implementation
+       /// and it's unsafe to continue.
+       ///
+       /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+       /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
+       DangerousValue,
 }
 
 /// An [`init`] message to be sent to or received from a peer.
@@ -396,6 +406,10 @@ pub struct ChannelReady {
        pub short_channel_id_alias: Option<u64>,
 }
 
+/// A randomly chosen number that is used to identify inputs within an interactive transaction
+/// construction.
+pub type SerialId = u64;
+
 /// An stfu (quiescence) message to be sent by or received from the stfu initiator.
 // TODO(splicing): Add spec link for `stfu`; still in draft, using from https://github.com/lightning/bolts/pull/863
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -459,7 +473,7 @@ pub struct TxAddInput {
        pub channel_id: ChannelId,
        /// A randomly chosen unique identifier for this input, which is even for initiators and odd for
        /// non-initiators.
-       pub serial_id: u64,
+       pub serial_id: SerialId,
        /// Serialized transaction that contains the output this input spends to verify that it is non
        /// malleable.
        pub prevtx: TransactionU16LenLimited,
@@ -478,7 +492,7 @@ pub struct TxAddOutput {
        pub channel_id: ChannelId,
        /// A randomly chosen unique identifier for this output, which is even for initiators and odd for
        /// non-initiators.
-       pub serial_id: u64,
+       pub serial_id: SerialId,
        /// The satoshi value of the output
        pub sats: u64,
        /// The scriptPubKey for the output
@@ -493,7 +507,7 @@ pub struct TxRemoveInput {
        /// The channel ID
        pub channel_id: ChannelId,
        /// The serial ID of the input to be removed
-       pub serial_id: u64,
+       pub serial_id: SerialId,
 }
 
 /// A tx_remove_output message for removing an output during interactive transaction construction.
@@ -504,7 +518,7 @@ pub struct TxRemoveOutput {
        /// The channel ID
        pub channel_id: ChannelId,
        /// The serial ID of the output to be removed
-       pub serial_id: u64,
+       pub serial_id: SerialId,
 }
 
 /// A tx_complete message signalling the conclusion of a peer's transaction contributions during
@@ -529,6 +543,8 @@ pub struct TxSignatures {
        pub tx_hash: Txid,
        /// The list of witnesses
        pub witnesses: Vec<Witness>,
+       /// Optional signature for the shared input -- the previous funding outpoint -- signed by both peers
+       pub funding_outpoint_sig: Option<Signature>,
 }
 
 /// A tx_init_rbf message which initiates a replacement of the transaction after it's been
@@ -1136,8 +1152,16 @@ pub struct UnsignedNodeAnnouncement {
        pub alias: NodeAlias,
        /// List of addresses on which this node is reachable
        pub addresses: Vec<SocketAddress>,
-       pub(crate) excess_address_data: Vec<u8>,
-       pub(crate) excess_data: Vec<u8>,
+       /// Excess address data which was signed as a part of the message which we do not (yet) understand how
+       /// to decode.
+       ///
+       /// This is stored to ensure forward-compatibility as new address types are added to the lightning gossip protocol.
+       pub excess_address_data: Vec<u8>,
+       /// Excess data which was signed as a part of the message which we do not (yet) understand how
+       /// to decode.
+       ///
+       /// This is stored to ensure forward-compatibility as new fields are added to the lightning gossip protocol.
+       pub excess_data: Vec<u8>,
 }
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
 /// A [`node_announcement`] message to be sent to or received from a peer.
@@ -1438,10 +1462,13 @@ pub trait ChannelMessageHandler : MessageSendEventsProvider {
 
        // Splicing
        /// Handle an incoming `splice` message from the given peer.
+       #[cfg(splicing)]
        fn handle_splice(&self, their_node_id: &PublicKey, msg: &Splice);
        /// Handle an incoming `splice_ack` message from the given peer.
+       #[cfg(splicing)]
        fn handle_splice_ack(&self, their_node_id: &PublicKey, msg: &SpliceAck);
        /// Handle an incoming `splice_locked` message from the given peer.
+       #[cfg(splicing)]
        fn handle_splice_locked(&self, their_node_id: &PublicKey, msg: &SpliceLocked);
 
        // Interactive channel construction
@@ -1650,11 +1677,13 @@ pub struct FinalOnionHopData {
 
 mod fuzzy_internal_msgs {
        use bitcoin::secp256k1::PublicKey;
-       use crate::blinded_path::payment::{PaymentConstraints, PaymentRelay};
-       use crate::prelude::*;
-       use crate::ln::{PaymentPreimage, PaymentSecret};
+       use crate::blinded_path::payment::{PaymentConstraints, PaymentContext, PaymentRelay};
+       use crate::ln::types::{PaymentPreimage, PaymentSecret};
        use crate::ln::features::BlindedHopFeatures;
-       use super::FinalOnionHopData;
+       use super::{FinalOnionHopData, TrampolineOnionPacket};
+
+       #[allow(unused_imports)]
+       use crate::prelude::*;
 
        // These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize
        // them from untrusted input):
@@ -1687,35 +1716,58 @@ mod fuzzy_internal_msgs {
                        cltv_expiry_height: u32,
                        payment_secret: PaymentSecret,
                        payment_constraints: PaymentConstraints,
+                       payment_context: PaymentContext,
                        intro_node_blinding_point: Option<PublicKey>,
+                       keysend_preimage: Option<PaymentPreimage>,
+                       custom_tlvs: Vec<(u64, Vec<u8>)>,
                }
        }
 
-       pub(crate) enum OutboundOnionPayload {
+       pub(crate) enum OutboundOnionPayload<'a> {
                Forward {
                        short_channel_id: u64,
                        /// The value, in msat, of the payment after this hop's fee is deducted.
                        amt_to_forward: u64,
                        outgoing_cltv_value: u32,
                },
+               #[allow(unused)]
+               TrampolineEntrypoint {
+                       amt_to_forward: u64,
+                       outgoing_cltv_value: u32,
+                       multipath_trampoline_data: Option<FinalOnionHopData>,
+                       trampoline_packet: TrampolineOnionPacket,
+               },
                Receive {
                        payment_data: Option<FinalOnionHopData>,
-                       payment_metadata: Option<Vec<u8>>,
+                       payment_metadata: Option<&'a Vec<u8>>,
                        keysend_preimage: Option<PaymentPreimage>,
-                       custom_tlvs: Vec<(u64, Vec<u8>)>,
+                       custom_tlvs: &'a Vec<(u64, Vec<u8>)>,
                        sender_intended_htlc_amt_msat: u64,
                        cltv_expiry_height: u32,
                },
                BlindedForward {
-                       encrypted_tlvs: Vec<u8>,
+                       encrypted_tlvs: &'a Vec<u8>,
                        intro_node_blinding_point: Option<PublicKey>,
                },
                BlindedReceive {
                        sender_intended_htlc_amt_msat: u64,
                        total_msat: u64,
                        cltv_expiry_height: u32,
-                       encrypted_tlvs: Vec<u8>,
+                       encrypted_tlvs: &'a Vec<u8>,
                        intro_node_blinding_point: Option<PublicKey>, // Set if the introduction node of the blinded path is the final node
+                       keysend_preimage: Option<PaymentPreimage>,
+                       custom_tlvs: &'a Vec<(u64, Vec<u8>)>,
+               }
+       }
+
+       pub(crate) enum OutboundTrampolinePayload {
+               #[allow(unused)]
+               Forward {
+                       /// The value, in msat, of the payment after this hop's fee is deducted.
+                       amt_to_forward: u64,
+                       outgoing_cltv_value: u32,
+                       /// The node id to which the trampoline node must find a route
+                       outgoing_node_id: PublicKey,
                }
        }
 
@@ -1765,6 +1817,72 @@ impl fmt::Debug for OnionPacket {
        }
 }
 
+/// BOLT 4 onion packet including hop data for the next peer.
+#[derive(Clone, Hash, PartialEq, Eq)]
+pub struct TrampolineOnionPacket {
+       /// Bolt 04 version number
+       pub version: u8,
+       /// A random sepc256k1 point, used to build the ECDH shared secret to decrypt hop_data
+       pub public_key: PublicKey,
+       /// Encrypted payload for the next hop
+       //
+       // Unlike the onion packets used for payments, Trampoline onion packets have to be shorter than
+       // 1300 bytes. The expected default is 650 bytes.
+       // TODO: if 650 ends up being the most common size, optimize this to be:
+       // enum { SixFifty([u8; 650]), VarLen(Vec<u8>) }
+       pub hop_data: Vec<u8>,
+       /// HMAC to verify the integrity of hop_data
+       pub hmac: [u8; 32],
+}
+
+impl onion_utils::Packet for TrampolineOnionPacket {
+       type Data = Vec<u8>;
+       fn new(public_key: PublicKey, hop_data: Vec<u8>, hmac: [u8; 32]) -> Self {
+               Self {
+                       version: 0,
+                       public_key,
+                       hop_data,
+                       hmac,
+               }
+       }
+}
+
+impl Writeable for TrampolineOnionPacket {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               self.version.write(w)?;
+               self.public_key.write(w)?;
+               w.write_all(&self.hop_data)?;
+               self.hmac.write(w)?;
+               Ok(())
+       }
+}
+
+impl LengthReadable for TrampolineOnionPacket {
+       fn read<R: LengthRead>(r: &mut R) -> Result<Self, DecodeError> {
+               let version = Readable::read(r)?;
+               let public_key = Readable::read(r)?;
+
+               let hop_data_len = r.total_bytes().saturating_sub(66); // 1 (version) + 33 (pubkey) + 32 (HMAC) = 66
+               let mut rd = FixedLengthReader::new(r, hop_data_len);
+               let hop_data = WithoutLength::<Vec<u8>>::read(&mut rd)?.0;
+
+               let hmac = Readable::read(r)?;
+
+               Ok(TrampolineOnionPacket {
+                       version,
+                       public_key,
+                       hop_data,
+                       hmac,
+               })
+       }
+}
+
+impl Debug for TrampolineOnionPacket {
+       fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+               f.write_fmt(format_args!("TrampolineOnionPacket version {} with hmac {:?}", self.version, &self.hmac[..]))
+       }
+}
+
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
 pub(crate) struct OnionErrorPacket {
        // This really should be a constant size slice, but the spec lets these things be up to 128KB?
@@ -1782,6 +1900,7 @@ impl fmt::Display for DecodeError {
                        DecodeError::BadLengthDescriptor => f.write_str("A length descriptor in the packet didn't describe the later data correctly"),
                        DecodeError::Io(ref e) => fmt::Debug::fmt(e, f),
                        DecodeError::UnsupportedCompression => f.write_str("We don't support receiving messages with zlib-compressed fields"),
+                       DecodeError::DangerousValue => f.write_str("Value would be dangerous to continue execution with"),
                }
        }
 }
@@ -2022,7 +2141,9 @@ impl_writeable_msg!(TxSignatures, {
        channel_id,
        tx_hash,
        witnesses,
-}, {});
+}, {
+       (0, funding_outpoint_sig, option),
+});
 
 impl_writeable_msg!(TxInitRbf, {
        channel_id,
@@ -2468,7 +2589,7 @@ impl Readable for FinalOnionHopData {
        }
 }
 
-impl Writeable for OutboundOnionPayload {
+impl<'a> Writeable for OutboundOnionPayload<'a> {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
                match self {
                        Self::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } => {
@@ -2478,6 +2599,17 @@ impl Writeable for OutboundOnionPayload {
                                        (6, short_channel_id, required)
                                });
                        },
+                       Self::TrampolineEntrypoint {
+                               amt_to_forward, outgoing_cltv_value, ref multipath_trampoline_data,
+                               ref trampoline_packet
+                       } => {
+                               _encode_varint_length_prefixed_tlv!(w, {
+                                       (2, HighZeroBytesDroppedBigSize(*amt_to_forward), required),
+                                       (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
+                                       (8, multipath_trampoline_data, option),
+                                       (20, trampoline_packet, required)
+                               });
+                       },
                        Self::Receive {
                                ref payment_data, ref payment_metadata, ref keysend_preimage, sender_intended_htlc_amt_msat,
                                cltv_expiry_height, ref custom_tlvs,
@@ -2492,32 +2624,54 @@ impl Writeable for OutboundOnionPayload {
                                        (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required),
                                        (4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required),
                                        (8, payment_data, option),
-                                       (16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option)
+                                       (16, payment_metadata.map(|m| WithoutLength(m)), option)
                                }, custom_tlvs.iter());
                        },
                        Self::BlindedForward { encrypted_tlvs, intro_node_blinding_point } => {
                                _encode_varint_length_prefixed_tlv!(w, {
-                                       (10, *encrypted_tlvs, required_vec),
+                                       (10, **encrypted_tlvs, required_vec),
                                        (12, intro_node_blinding_point, option)
                                });
                        },
                        Self::BlindedReceive {
                                sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, encrypted_tlvs,
-                               intro_node_blinding_point,
+                               intro_node_blinding_point, keysend_preimage, ref custom_tlvs,
                        } => {
+                               // We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`]
+                               // to reject any reserved types in the experimental range if new ones are ever
+                               // standardized.
+                               let keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode()));
+                               let mut custom_tlvs: Vec<&(u64, Vec<u8>)> = custom_tlvs.iter().chain(keysend_tlv.iter()).collect();
+                               custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ);
                                _encode_varint_length_prefixed_tlv!(w, {
                                        (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required),
                                        (4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required),
-                                       (10, *encrypted_tlvs, required_vec),
+                                       (10, **encrypted_tlvs, required_vec),
                                        (12, intro_node_blinding_point, option),
                                        (18, HighZeroBytesDroppedBigSize(*total_msat), required)
-                               });
+                               }, custom_tlvs.iter());
                        },
                }
                Ok(())
        }
 }
 
+impl Writeable for OutboundTrampolinePayload {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               match self {
+                       Self::Forward { amt_to_forward, outgoing_cltv_value, outgoing_node_id } => {
+                               _encode_varint_length_prefixed_tlv!(w, {
+                                       (2, HighZeroBytesDroppedBigSize(*amt_to_forward), required),
+                                       (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
+                                       (14, outgoing_node_id, required)
+                               });
+                       }
+               }
+               Ok(())
+       }
+}
+
+
 impl<NS: Deref> ReadableArgs<(Option<PublicKey>, &NS)> for InboundOnionPayload where NS::Target: NodeSigner {
        fn read<R: Read>(r: &mut R, args: (Option<PublicKey>, &NS)) -> Result<Self, DecodeError> {
                let (update_add_blinding_point, node_signer) = args;
@@ -2560,9 +2714,7 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, &NS)> for InboundOnionPayload w
                }
 
                if let Some(blinding_point) = intro_node_blinding_point.or(update_add_blinding_point) {
-                       if short_id.is_some() || payment_data.is_some() || payment_metadata.is_some() ||
-                               keysend_preimage.is_some()
-                       {
+                       if short_id.is_some() || payment_data.is_some() || payment_metadata.is_some() {
                                return Err(DecodeError::InvalidValue)
                        }
                        let enc_tlvs = encrypted_tlvs_opt.ok_or(DecodeError::InvalidValue)?.0;
@@ -2575,7 +2727,9 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, &NS)> for InboundOnionPayload w
                                ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Forward(ForwardTlvs {
                                        short_channel_id, payment_relay, payment_constraints, features
                                })} => {
-                                       if amt.is_some() || cltv_value.is_some() || total_msat.is_some() {
+                                       if amt.is_some() || cltv_value.is_some() || total_msat.is_some() ||
+                                               keysend_preimage.is_some()
+                                       {
                                                return Err(DecodeError::InvalidValue)
                                        }
                                        Ok(Self::BlindedForward {
@@ -2587,7 +2741,7 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, &NS)> for InboundOnionPayload w
                                        })
                                },
                                ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(ReceiveTlvs {
-                                       payment_secret, payment_constraints
+                                       payment_secret, payment_constraints, payment_context
                                })} => {
                                        if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
                                        Ok(Self::BlindedReceive {
@@ -2596,7 +2750,10 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, &NS)> for InboundOnionPayload w
                                                cltv_expiry_height: cltv_value.ok_or(DecodeError::InvalidValue)?,
                                                payment_secret,
                                                payment_constraints,
+                                               payment_context,
                                                intro_node_blinding_point,
+                                               keysend_preimage,
+                                               custom_tlvs,
                                        })
                                },
                        }
@@ -3037,26 +3194,25 @@ impl_writeable_msg!(GossipTimestampFilter, {
 
 #[cfg(test)]
 mod tests {
-       use std::convert::TryFrom;
-       use bitcoin::{Transaction, TxIn, ScriptBuf, Sequence, Witness, TxOut};
+       use bitcoin::{Amount, Transaction, TxIn, ScriptBuf, Sequence, Witness, TxOut};
        use hex::DisplayHex;
-       use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
-       use crate::ln::ChannelId;
+       use crate::ln::types::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
        use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
-       use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields};
+       use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields, TrampolineOnionPacket};
        use crate::ln::msgs::SocketAddress;
        use crate::routing::gossip::{NodeAlias, NodeId};
-       use crate::util::ser::{Writeable, Readable, ReadableArgs, Hostname, TransactionU16LenLimited};
+       use crate::util::ser::{BigSize, FixedLengthReader, Hostname, LengthReadable, Readable, ReadableArgs, TransactionU16LenLimited, Writeable};
        use crate::util::test_utils;
 
        use bitcoin::hashes::hex::FromHex;
        use bitcoin::address::Address;
-       use bitcoin::network::constants::Network;
+       use bitcoin::network::Network;
        use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::blockdata::script::Builder;
        use bitcoin::blockdata::opcodes;
        use bitcoin::hash_types::Txid;
        use bitcoin::locktime::absolute::LockTime;
+       use bitcoin::transaction::Version;
 
        use bitcoin::secp256k1::{PublicKey,SecretKey};
        use bitcoin::secp256k1::{Secp256k1, Message};
@@ -3147,7 +3303,7 @@ mod tests {
        macro_rules! get_sig_on {
                ($privkey: expr, $ctx: expr, $string: expr) => {
                        {
-                               let sighash = Message::from_slice(&$string.into_bytes()[..]).unwrap();
+                               let sighash = Message::from_digest_slice(&$string.into_bytes()[..]).unwrap();
                                $ctx.sign_ecdsa(&sighash, &$privkey)
                        }
                }
@@ -3749,7 +3905,7 @@ mod tests {
                        channel_id: ChannelId::from_bytes([2; 32]),
                        serial_id: 4886718345,
                        prevtx: TransactionU16LenLimited::new(Transaction {
-                               version: 2,
+                               version: Version::TWO,
                                lock_time: LockTime::ZERO,
                                input: vec![TxIn {
                                        previous_output: OutPoint { txid: Txid::from_str("305bab643ee297b8b6b76b320792c8223d55082122cb606bf89382146ced9c77").unwrap(), index: 2 }.into_bitcoin_outpoint(),
@@ -3761,12 +3917,12 @@ mod tests {
                                }],
                                output: vec![
                                        TxOut {
-                                               value: 12704566,
-                                               script_pubkey: Address::from_str("bc1qzlffunw52jav8vwdu5x3jfk6sr8u22rmq3xzw2").unwrap().payload.script_pubkey(),
+                                               value: Amount::from_sat(12704566),
+                                               script_pubkey: Address::from_str("bc1qzlffunw52jav8vwdu5x3jfk6sr8u22rmq3xzw2").unwrap().payload().script_pubkey(),
                                        },
                                        TxOut {
-                                               value: 245148,
-                                               script_pubkey: Address::from_str("bc1qxmk834g5marzm227dgqvynd23y2nvt2ztwcw2z").unwrap().payload.script_pubkey(),
+                                               value: Amount::from_sat(245148),
+                                               script_pubkey: Address::from_str("bc1qxmk834g5marzm227dgqvynd23y2nvt2ztwcw2z").unwrap().payload().script_pubkey(),
                                        },
                                ],
                        }).unwrap(),
@@ -3784,7 +3940,7 @@ mod tests {
                        channel_id: ChannelId::from_bytes([2; 32]),
                        serial_id: 4886718345,
                        sats: 4886718345,
-                       script: Address::from_str("bc1qxmk834g5marzm227dgqvynd23y2nvt2ztwcw2z").unwrap().payload.script_pubkey(),
+                       script: Address::from_str("bc1qxmk834g5marzm227dgqvynd23y2nvt2ztwcw2z").unwrap().payload().script_pubkey(),
                };
                let encoded_value = tx_add_output.encode();
                let target_value = <Vec<u8>>::from_hex("0202020202020202020202020202020202020202020202020202020202020202000000012345678900000001234567890016001436ec78d514df462da95e6a00c24daa8915362d42").unwrap();
@@ -3825,6 +3981,10 @@ mod tests {
 
        #[test]
        fn encoding_tx_signatures() {
+               let secp_ctx = Secp256k1::new();
+               let (privkey_1, _) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
+               let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
+
                let tx_signatures = msgs::TxSignatures {
                        channel_id: ChannelId::from_bytes([2; 32]),
                        tx_hash: Txid::from_str("c2d4449afa8d26140898dd54d3390b057ba2a5afcf03ba29d7dc0d8b9ffe966e").unwrap(),
@@ -3836,6 +3996,7 @@ mod tests {
                                        <Vec<u8>>::from_hex("3045022100ee00dbf4a862463e837d7c08509de814d620e4d9830fa84818713e0fa358f145022021c3c7060c4d53fe84fd165d60208451108a778c13b92ca4c6bad439236126cc01").unwrap(),
                                        <Vec<u8>>::from_hex("028fbbf0b16f5ba5bcb5dd37cd4047ce6f726a21c06682f9ec2f52b057de1dbdb5").unwrap()]),
                        ],
+                       funding_outpoint_sig: Some(sig_1),
                };
                let encoded_value = tx_signatures.encode();
                let mut target_value = <Vec<u8>>::from_hex("0202020202020202020202020202020202020202020202020202020202020202").unwrap(); // channel_id
@@ -3855,6 +4016,8 @@ mod tests {
                target_value.append(&mut <Vec<u8>>::from_hex("3045022100ee00dbf4a862463e837d7c08509de814d620e4d9830fa84818713e0fa358f145022021c3c7060c4d53fe84fd165d60208451108a778c13b92ca4c6bad439236126cc01").unwrap());
                target_value.append(&mut <Vec<u8>>::from_hex("21").unwrap()); // len of witness element data (VarInt)
                target_value.append(&mut <Vec<u8>>::from_hex("028fbbf0b16f5ba5bcb5dd37cd4047ce6f726a21c06682f9ec2f52b057de1dbdb5").unwrap());
+               target_value.append(&mut <Vec<u8>>::from_hex("0040").unwrap()); // type and len (64)
+               target_value.append(&mut <Vec<u8>>::from_hex("d977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a").unwrap());
                assert_eq!(encoded_value, target_value);
        }
 
@@ -4217,7 +4380,7 @@ mod tests {
                        keysend_preimage: None,
                        sender_intended_htlc_amt_msat: 0x0badf00d01020304,
                        cltv_expiry_height: 0xffffffff,
-                       custom_tlvs: vec![],
+                       custom_tlvs: &vec![],
                };
                let encoded_value = outbound_msg.encode();
                let target_value = <Vec<u8>>::from_hex("1002080badf00d010203040404ffffffff").unwrap();
@@ -4245,7 +4408,7 @@ mod tests {
                        keysend_preimage: None,
                        sender_intended_htlc_amt_msat: 0x0badf00d01020304,
                        cltv_expiry_height: 0xffffffff,
-                       custom_tlvs: vec![],
+                       custom_tlvs: &vec![],
                };
                let encoded_value = outbound_msg.encode();
                let target_value = <Vec<u8>>::from_hex("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap();
@@ -4282,7 +4445,7 @@ mod tests {
                        payment_data: None,
                        payment_metadata: None,
                        keysend_preimage: None,
-                       custom_tlvs: bad_type_range_tlvs,
+                       custom_tlvs: &bad_type_range_tlvs,
                        sender_intended_htlc_amt_msat: 0x0badf00d01020304,
                        cltv_expiry_height: 0xffffffff,
                };
@@ -4294,7 +4457,7 @@ mod tests {
                        ((1 << 16) - 1, vec![42; 32]),
                ];
                if let msgs::OutboundOnionPayload::Receive { ref mut custom_tlvs, .. } = msg {
-                       *custom_tlvs = good_type_range_tlvs.clone();
+                       *custom_tlvs = &good_type_range_tlvs;
                }
                let encoded_value = msg.encode();
                let inbound_msg = ReadableArgs::read(&mut Cursor::new(&encoded_value[..]), (None, &&node_signer)).unwrap();
@@ -4314,7 +4477,7 @@ mod tests {
                        payment_data: None,
                        payment_metadata: None,
                        keysend_preimage: None,
-                       custom_tlvs: expected_custom_tlvs.clone(),
+                       custom_tlvs: &expected_custom_tlvs,
                        sender_intended_htlc_amt_msat: 0x0badf00d01020304,
                        cltv_expiry_height: 0xffffffff,
                };
@@ -4338,6 +4501,71 @@ mod tests {
                } else { panic!(); }
        }
 
+       #[test]
+       fn encoding_final_onion_hop_data_with_trampoline_packet() {
+               let secp_ctx = Secp256k1::new();
+               let (_private_key, public_key) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
+
+               let compressed_public_key = public_key.serialize();
+               assert_eq!(compressed_public_key.len(), 33);
+
+               let trampoline_packet = TrampolineOnionPacket {
+                       version: 0,
+                       public_key,
+                       hop_data: vec![1; 650], // this should be the standard encoded length
+                       hmac: [2; 32],
+               };
+               let encoded_trampoline_packet = trampoline_packet.encode();
+               assert_eq!(encoded_trampoline_packet.len(), 716);
+
+               { // verify that a codec round trip works
+                       let mut reader = Cursor::new(&encoded_trampoline_packet);
+                       let mut trampoline_packet_reader = FixedLengthReader::new(&mut reader, encoded_trampoline_packet.len() as u64);
+                       let decoded_trampoline_packet: TrampolineOnionPacket = <TrampolineOnionPacket as LengthReadable>::read(&mut trampoline_packet_reader).unwrap();
+                       assert_eq!(decoded_trampoline_packet.encode(), encoded_trampoline_packet);
+               }
+
+               let msg = msgs::OutboundOnionPayload::TrampolineEntrypoint {
+                       multipath_trampoline_data: None,
+                       amt_to_forward: 0x0badf00d01020304,
+                       outgoing_cltv_value: 0xffffffff,
+                       trampoline_packet,
+               };
+               let encoded_payload = msg.encode();
+
+               let trampoline_type_bytes = &encoded_payload[19..=19];
+               let mut trampoline_type_cursor = Cursor::new(trampoline_type_bytes);
+               let trampoline_type_big_size: BigSize = Readable::read(&mut trampoline_type_cursor).unwrap();
+               assert_eq!(trampoline_type_big_size.0, 20);
+
+               let trampoline_length_bytes = &encoded_payload[20..=22];
+               let mut trampoline_length_cursor = Cursor::new(trampoline_length_bytes);
+               let trampoline_length_big_size: BigSize = Readable::read(&mut trampoline_length_cursor).unwrap();
+               assert_eq!(trampoline_length_big_size.0, encoded_trampoline_packet.len() as u64);
+       }
+
+       #[test]
+       fn encoding_final_onion_hop_data_with_eclair_trampoline_packet() {
+               let public_key = PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()).unwrap();
+               let hop_data = <Vec<u8>>::from_hex("cff34152f3a36e52ca94e74927203a560392b9cc7ce3c45809c6be52166c24a595716880f95f178bf5b30ca63141f74db6e92795c6130877cfdac3d4bd3087ee73c65d627ddd709112a848cc99e303f3706509aa43ba7c8a88cba175fccf9a8f5016ef06d3b935dbb15196d7ce16dc1a7157845566901d7b2197e52cab4ce487014b14816e5805f9fcacb4f8f88b8ff176f1b94f6ce6b00bc43221130c17d20ef629db7c5f7eafaa166578c720619561dd14b3277db557ec7dcdb793771aef0f2f667cfdbeae3ac8d331c5994779dffb31e5fc0dbdedc0c592ca6d21c18e47fe3528d6975c19517d7e2ea8c5391cf17d0fe30c80913ed887234ccb48808f7ef9425bcd815c3b586210979e3bb286ef2851bf9ce04e28c40a203df98fd648d2f1936fd2f1def0e77eecb277229b4b682322371c0a1dbfcd723a991993df8cc1f2696b84b055b40a1792a29f710295a18fbd351b0f3ff34cd13941131b8278ba79303c89117120eea691738a9954908195143b039dbeed98f26a92585f3d15cf742c953799d3272e0545e9b744be9d3b4c").unwrap();
+               let hmac_vector = <Vec<u8>>::from_hex("bb079bfc4b35190eee9f59a1d7b41ba2f773179f322dafb4b1af900c289ebd6c").unwrap();
+               let mut hmac = [0; 32];
+               hmac.copy_from_slice(&hmac_vector);
+
+               let compressed_public_key = public_key.serialize();
+               assert_eq!(compressed_public_key.len(), 33);
+
+               let trampoline_packet = TrampolineOnionPacket {
+                       version: 0,
+                       public_key,
+                       hop_data,
+                       hmac,
+               };
+               let encoded_trampoline_packet = trampoline_packet.encode();
+               let expected_eclair_trampoline_packet = <Vec<u8>>::from_hex("0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619cff34152f3a36e52ca94e74927203a560392b9cc7ce3c45809c6be52166c24a595716880f95f178bf5b30ca63141f74db6e92795c6130877cfdac3d4bd3087ee73c65d627ddd709112a848cc99e303f3706509aa43ba7c8a88cba175fccf9a8f5016ef06d3b935dbb15196d7ce16dc1a7157845566901d7b2197e52cab4ce487014b14816e5805f9fcacb4f8f88b8ff176f1b94f6ce6b00bc43221130c17d20ef629db7c5f7eafaa166578c720619561dd14b3277db557ec7dcdb793771aef0f2f667cfdbeae3ac8d331c5994779dffb31e5fc0dbdedc0c592ca6d21c18e47fe3528d6975c19517d7e2ea8c5391cf17d0fe30c80913ed887234ccb48808f7ef9425bcd815c3b586210979e3bb286ef2851bf9ce04e28c40a203df98fd648d2f1936fd2f1def0e77eecb277229b4b682322371c0a1dbfcd723a991993df8cc1f2696b84b055b40a1792a29f710295a18fbd351b0f3ff34cd13941131b8278ba79303c89117120eea691738a9954908195143b039dbeed98f26a92585f3d15cf742c953799d3272e0545e9b744be9d3b4cbb079bfc4b35190eee9f59a1d7b41ba2f773179f322dafb4b1af900c289ebd6c").unwrap();
+               assert_eq!(encoded_trampoline_packet, expected_eclair_trampoline_packet);
+       }
+
        #[test]
        fn query_channel_range_end_blocknum() {
                let tests: Vec<(u32, u32, u32)> = vec![
index e75bd2c70e1e9c3d6b68945fa0ea424a694d4f7d..eedd82c569be4acdb1c0d50390db02c7819e5b18 100644 (file)
 //! Nodes without channels are disconnected and connected as needed to ensure that deterministic
 //! blinded paths are used.
 
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
+use bitcoin::secp256k1::PublicKey;
 use core::time::Duration;
-use crate::blinded_path::BlindedPath;
+use crate::blinded_path::{BlindedPath, IntroductionNode};
+use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentContext};
 use crate::events::{Event, MessageSendEventsProvider, PaymentPurpose};
 use crate::ln::channelmanager::{PaymentId, RecentPaymentDetails, Retry, self};
 use crate::ln::functional_test_utils::*;
 use crate::ln::msgs::{ChannelMessageHandler, Init, NodeAnnouncement, OnionMessage, OnionMessageHandler, RoutingMessageHandler, SocketAddress, UnsignedGossipMessage, UnsignedNodeAnnouncement};
 use crate::offers::invoice::Bolt12Invoice;
 use crate::offers::invoice_error::InvoiceError;
-use crate::offers::invoice_request::InvoiceRequest;
+use crate::offers::invoice_request::{InvoiceRequest, InvoiceRequestFields};
 use crate::offers::parse::Bolt12SemanticError;
 use crate::onion_message::messenger::PeeledOnion;
 use crate::onion_message::offers::OffersMessage;
@@ -132,6 +134,12 @@ fn announce_node_address<'a, 'b, 'c>(
        }
 }
 
+fn resolve_introduction_node<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, path: &BlindedPath) -> PublicKey {
+       path.public_introduction_node_id(&node.network_graph.read_only())
+               .and_then(|node_id| node_id.as_pubkey().ok())
+               .unwrap()
+}
+
 fn route_bolt12_payment<'a, 'b, 'c>(
        node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>], invoice: &Bolt12Invoice
 ) {
@@ -151,25 +159,37 @@ fn route_bolt12_payment<'a, 'b, 'c>(
        do_pass_along_path(args);
 }
 
-fn claim_bolt12_payment<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>]) {
+fn claim_bolt12_payment<'a, 'b, 'c>(
+       node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>], expected_payment_context: PaymentContext
+) {
        let recipient = &path[path.len() - 1];
-       match get_event!(recipient, Event::PaymentClaimable) {
-               Event::PaymentClaimable {
-                       purpose: PaymentPurpose::InvoicePayment {
-                               payment_preimage: Some(payment_preimage), ..
-                       }, ..
-               } => claim_payment(node, path, payment_preimage),
-               _ => panic!(),
+       let payment_purpose = match get_event!(recipient, Event::PaymentClaimable) {
+               Event::PaymentClaimable { purpose, .. } => purpose,
+               _ => panic!("No Event::PaymentClaimable"),
+       };
+       let payment_preimage = match payment_purpose.preimage() {
+               Some(preimage) => preimage,
+               None => panic!("No preimage in Event::PaymentClaimable"),
        };
+       match payment_purpose {
+               PaymentPurpose::Bolt12OfferPayment { payment_context, .. } => {
+                       assert_eq!(PaymentContext::Bolt12Offer(payment_context), expected_payment_context);
+               },
+               PaymentPurpose::Bolt12RefundPayment { payment_context, .. } => {
+                       assert_eq!(PaymentContext::Bolt12Refund(payment_context), expected_payment_context);
+               },
+               _ => panic!("Unexpected payment purpose: {:?}", payment_purpose),
+       }
+       claim_payment(node, path, payment_preimage);
 }
 
 fn extract_invoice_request<'a, 'b, 'c>(
        node: &Node<'a, 'b, 'c>, message: &OnionMessage
-) -> (InvoiceRequest, Option<BlindedPath>) {
+) -> (InvoiceRequest, BlindedPath) {
        match node.onion_messenger.peel_onion_message(message) {
                Ok(PeeledOnion::Receive(message, _, reply_path)) => match message {
                        ParsedOnionMessageContents::Offers(offers_message) => match offers_message {
-                               OffersMessage::InvoiceRequest(invoice_request) => (invoice_request, reply_path),
+                               OffersMessage::InvoiceRequest(invoice_request) => (invoice_request, reply_path.unwrap()),
                                OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice),
                                OffersMessage::InvoiceError(error) => panic!("Unexpected invoice_error: {:?}", error),
                        },
@@ -254,14 +274,15 @@ fn prefers_non_tor_nodes_in_blinded_paths() {
        announce_node_address(charlie, &[alice, bob, david, &nodes[4], &nodes[5]], tor.clone());
 
        let offer = bob.node
-               .create_offer_builder("coffee".to_string()).unwrap()
+               .create_offer_builder().unwrap()
                .amount_msats(10_000_000)
                .build().unwrap();
-       assert_ne!(offer.signing_pubkey(), bob_id);
+       assert_ne!(offer.signing_pubkey(), Some(bob_id));
        assert!(!offer.paths().is_empty());
        for path in offer.paths() {
-               assert_ne!(path.introduction_node_id, bob_id);
-               assert_ne!(path.introduction_node_id, charlie_id);
+               let introduction_node_id = resolve_introduction_node(david, &path);
+               assert_ne!(introduction_node_id, bob_id);
+               assert_ne!(introduction_node_id, charlie_id);
        }
 
        // Use a one-hop blinded path when Bob is announced and all his peers are Tor-only.
@@ -269,13 +290,14 @@ fn prefers_non_tor_nodes_in_blinded_paths() {
        announce_node_address(&nodes[5], &[alice, bob, charlie, david, &nodes[4]], tor.clone());
 
        let offer = bob.node
-               .create_offer_builder("coffee".to_string()).unwrap()
+               .create_offer_builder().unwrap()
                .amount_msats(10_000_000)
                .build().unwrap();
-       assert_ne!(offer.signing_pubkey(), bob_id);
+       assert_ne!(offer.signing_pubkey(), Some(bob_id));
        assert!(!offer.paths().is_empty());
        for path in offer.paths() {
-               assert_eq!(path.introduction_node_id, bob_id);
+               let introduction_node_id = resolve_introduction_node(david, &path);
+               assert_eq!(introduction_node_id, bob_id);
        }
 }
 
@@ -319,13 +341,14 @@ fn prefers_more_connected_nodes_in_blinded_paths() {
        disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
 
        let offer = bob.node
-               .create_offer_builder("coffee".to_string()).unwrap()
+               .create_offer_builder().unwrap()
                .amount_msats(10_000_000)
                .build().unwrap();
-       assert_ne!(offer.signing_pubkey(), bob_id);
+       assert_ne!(offer.signing_pubkey(), Some(bob_id));
        assert!(!offer.paths().is_empty());
        for path in offer.paths() {
-               assert_eq!(path.introduction_node_id, nodes[4].node.get_our_node_id());
+               let introduction_node_id = resolve_introduction_node(david, &path);
+               assert_eq!(introduction_node_id, nodes[4].node.get_our_node_id());
        }
 }
 
@@ -368,13 +391,16 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
        disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
 
        let offer = alice.node
-               .create_offer_builder("coffee".to_string()).unwrap()
+               .create_offer_builder()
+               .unwrap()
                .amount_msats(10_000_000)
                .build().unwrap();
-       assert_ne!(offer.signing_pubkey(), alice_id);
+       assert_ne!(offer.signing_pubkey(), Some(alice_id));
        assert!(!offer.paths().is_empty());
        for path in offer.paths() {
-               assert_eq!(path.introduction_node_id, bob_id);
+               let introduction_node_id = resolve_introduction_node(david, &path);
+               assert_eq!(introduction_node_id, bob_id);
+               assert!(matches!(path.introduction_node, IntroductionNode::DirectedShortChannelId(..)));
        }
 
        let payment_id = PaymentId([1; 32]);
@@ -393,9 +419,19 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
        alice.onion_messenger.handle_onion_message(&bob_id, &onion_message);
 
        let (invoice_request, reply_path) = extract_invoice_request(alice, &onion_message);
+       let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext {
+               offer_id: offer.id(),
+               invoice_request: InvoiceRequestFields {
+                       payer_id: invoice_request.payer_id(),
+                       quantity: None,
+                       payer_note_truncated: None,
+               },
+       });
+       let introduction_node_id = resolve_introduction_node(alice, &reply_path);
        assert_eq!(invoice_request.amount_msats(), None);
        assert_ne!(invoice_request.payer_id(), david_id);
-       assert_eq!(reply_path.unwrap().introduction_node_id, charlie_id);
+       assert_eq!(introduction_node_id, charlie_id);
+       assert!(matches!(reply_path.introduction_node, IntroductionNode::DirectedShortChannelId(..)));
 
        let onion_message = alice.onion_messenger.next_onion_message_for_peer(charlie_id).unwrap();
        charlie.onion_messenger.handle_onion_message(&alice_id, &onion_message);
@@ -408,13 +444,13 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
        assert_ne!(invoice.signing_pubkey(), alice_id);
        assert!(!invoice.payment_paths().is_empty());
        for (_, path) in invoice.payment_paths() {
-               assert_eq!(path.introduction_node_id, bob_id);
+               assert_eq!(path.introduction_node, IntroductionNode::NodeId(bob_id));
        }
 
        route_bolt12_payment(david, &[charlie, bob, alice], &invoice);
        expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
 
-       claim_bolt12_payment(david, &[charlie, bob, alice]);
+       claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
        expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
 }
 
@@ -459,9 +495,7 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() {
        let absolute_expiry = Duration::from_secs(u64::MAX);
        let payment_id = PaymentId([1; 32]);
        let refund = david.node
-               .create_refund_builder(
-                       "refund".to_string(), 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
-               )
+               .create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
                .unwrap()
                .build().unwrap();
        assert_eq!(refund.amount_msats(), 10_000_000);
@@ -469,11 +503,14 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() {
        assert_ne!(refund.payer_id(), david_id);
        assert!(!refund.paths().is_empty());
        for path in refund.paths() {
-               assert_eq!(path.introduction_node_id, charlie_id);
+               let introduction_node_id = resolve_introduction_node(alice, &path);
+               assert_eq!(introduction_node_id, charlie_id);
+               assert!(matches!(path.introduction_node, IntroductionNode::DirectedShortChannelId(..)));
        }
        expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
 
-       alice.node.request_refund_payment(&refund).unwrap();
+       let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {});
+       let expected_invoice = alice.node.request_refund_payment(&refund).unwrap();
 
        connect_peers(alice, charlie);
 
@@ -484,17 +521,19 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() {
        david.onion_messenger.handle_onion_message(&charlie_id, &onion_message);
 
        let invoice = extract_invoice(david, &onion_message);
+       assert_eq!(invoice, expected_invoice);
+
        assert_eq!(invoice.amount_msats(), 10_000_000);
        assert_ne!(invoice.signing_pubkey(), alice_id);
        assert!(!invoice.payment_paths().is_empty());
        for (_, path) in invoice.payment_paths() {
-               assert_eq!(path.introduction_node_id, bob_id);
+               assert_eq!(path.introduction_node, IntroductionNode::NodeId(bob_id));
        }
 
        route_bolt12_payment(david, &[charlie, bob, alice], &invoice);
        expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
 
-       claim_bolt12_payment(david, &[charlie, bob, alice]);
+       claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
        expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
 }
 
@@ -516,13 +555,15 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
        let bob_id = bob.node.get_our_node_id();
 
        let offer = alice.node
-               .create_offer_builder("coffee".to_string()).unwrap()
+               .create_offer_builder().unwrap()
                .amount_msats(10_000_000)
                .build().unwrap();
-       assert_ne!(offer.signing_pubkey(), alice_id);
+       assert_ne!(offer.signing_pubkey(), Some(alice_id));
        assert!(!offer.paths().is_empty());
        for path in offer.paths() {
-               assert_eq!(path.introduction_node_id, alice_id);
+               let introduction_node_id = resolve_introduction_node(bob, &path);
+               assert_eq!(introduction_node_id, alice_id);
+               assert!(matches!(path.introduction_node, IntroductionNode::DirectedShortChannelId(..)));
        }
 
        let payment_id = PaymentId([1; 32]);
@@ -533,9 +574,19 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
        alice.onion_messenger.handle_onion_message(&bob_id, &onion_message);
 
        let (invoice_request, reply_path) = extract_invoice_request(alice, &onion_message);
+       let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext {
+               offer_id: offer.id(),
+               invoice_request: InvoiceRequestFields {
+                       payer_id: invoice_request.payer_id(),
+                       quantity: None,
+                       payer_note_truncated: None,
+               },
+       });
+       let introduction_node_id = resolve_introduction_node(alice, &reply_path);
        assert_eq!(invoice_request.amount_msats(), None);
        assert_ne!(invoice_request.payer_id(), bob_id);
-       assert_eq!(reply_path.unwrap().introduction_node_id, bob_id);
+       assert_eq!(introduction_node_id, bob_id);
+       assert!(matches!(reply_path.introduction_node, IntroductionNode::DirectedShortChannelId(..)));
 
        let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
        bob.onion_messenger.handle_onion_message(&alice_id, &onion_message);
@@ -545,13 +596,13 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
        assert_ne!(invoice.signing_pubkey(), alice_id);
        assert!(!invoice.payment_paths().is_empty());
        for (_, path) in invoice.payment_paths() {
-               assert_eq!(path.introduction_node_id, alice_id);
+               assert_eq!(path.introduction_node, IntroductionNode::NodeId(alice_id));
        }
 
        route_bolt12_payment(bob, &[alice], &invoice);
        expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
 
-       claim_bolt12_payment(bob, &[alice]);
+       claim_bolt12_payment(bob, &[alice], payment_context);
        expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
 }
 
@@ -575,9 +626,7 @@ fn creates_and_pays_for_refund_using_one_hop_blinded_path() {
        let absolute_expiry = Duration::from_secs(u64::MAX);
        let payment_id = PaymentId([1; 32]);
        let refund = bob.node
-               .create_refund_builder(
-                       "refund".to_string(), 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
-               )
+               .create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
                .unwrap()
                .build().unwrap();
        assert_eq!(refund.amount_msats(), 10_000_000);
@@ -585,27 +634,32 @@ fn creates_and_pays_for_refund_using_one_hop_blinded_path() {
        assert_ne!(refund.payer_id(), bob_id);
        assert!(!refund.paths().is_empty());
        for path in refund.paths() {
-               assert_eq!(path.introduction_node_id, bob_id);
+               let introduction_node_id = resolve_introduction_node(alice, &path);
+               assert_eq!(introduction_node_id, bob_id);
+               assert!(matches!(path.introduction_node, IntroductionNode::DirectedShortChannelId(..)));
        }
        expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
 
-       alice.node.request_refund_payment(&refund).unwrap();
+       let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {});
+       let expected_invoice = alice.node.request_refund_payment(&refund).unwrap();
 
        let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
        bob.onion_messenger.handle_onion_message(&alice_id, &onion_message);
 
        let invoice = extract_invoice(bob, &onion_message);
+       assert_eq!(invoice, expected_invoice);
+
        assert_eq!(invoice.amount_msats(), 10_000_000);
        assert_ne!(invoice.signing_pubkey(), alice_id);
        assert!(!invoice.payment_paths().is_empty());
        for (_, path) in invoice.payment_paths() {
-               assert_eq!(path.introduction_node_id, alice_id);
+               assert_eq!(path.introduction_node, IntroductionNode::NodeId(alice_id));
        }
 
        route_bolt12_payment(bob, &[alice], &invoice);
        expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
 
-       claim_bolt12_payment(bob, &[alice]);
+       claim_bolt12_payment(bob, &[alice], payment_context);
        expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
 }
 
@@ -627,11 +681,11 @@ fn pays_for_offer_without_blinded_paths() {
        let bob_id = bob.node.get_our_node_id();
 
        let offer = alice.node
-               .create_offer_builder("coffee".to_string()).unwrap()
+               .create_offer_builder().unwrap()
                .clear_paths()
                .amount_msats(10_000_000)
                .build().unwrap();
-       assert_eq!(offer.signing_pubkey(), alice_id);
+       assert_eq!(offer.signing_pubkey(), Some(alice_id));
        assert!(offer.paths().is_empty());
 
        let payment_id = PaymentId([1; 32]);
@@ -641,6 +695,16 @@ fn pays_for_offer_without_blinded_paths() {
        let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
        alice.onion_messenger.handle_onion_message(&bob_id, &onion_message);
 
+       let (invoice_request, _) = extract_invoice_request(alice, &onion_message);
+       let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext {
+               offer_id: offer.id(),
+               invoice_request: InvoiceRequestFields {
+                       payer_id: invoice_request.payer_id(),
+                       quantity: None,
+                       payer_note_truncated: None,
+               },
+       });
+
        let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
        bob.onion_messenger.handle_onion_message(&alice_id, &onion_message);
 
@@ -648,7 +712,7 @@ fn pays_for_offer_without_blinded_paths() {
        route_bolt12_payment(bob, &[alice], &invoice);
        expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
 
-       claim_bolt12_payment(bob, &[alice]);
+       claim_bolt12_payment(bob, &[alice], payment_context);
        expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
 }
 
@@ -671,9 +735,7 @@ fn pays_for_refund_without_blinded_paths() {
        let absolute_expiry = Duration::from_secs(u64::MAX);
        let payment_id = PaymentId([1; 32]);
        let refund = bob.node
-               .create_refund_builder(
-                       "refund".to_string(), 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
-               )
+               .create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
                .unwrap()
                .clear_paths()
                .build().unwrap();
@@ -681,16 +743,19 @@ fn pays_for_refund_without_blinded_paths() {
        assert!(refund.paths().is_empty());
        expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
 
-       alice.node.request_refund_payment(&refund).unwrap();
+       let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {});
+       let expected_invoice = alice.node.request_refund_payment(&refund).unwrap();
 
        let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
        bob.onion_messenger.handle_onion_message(&alice_id, &onion_message);
 
        let invoice = extract_invoice(bob, &onion_message);
+       assert_eq!(invoice, expected_invoice);
+
        route_bolt12_payment(bob, &[alice], &invoice);
        expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
 
-       claim_bolt12_payment(bob, &[alice]);
+       claim_bolt12_payment(bob, &[alice], payment_context);
        expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
 }
 
@@ -704,7 +769,7 @@ fn fails_creating_offer_without_blinded_paths() {
 
        create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
 
-       match nodes[0].node.create_offer_builder("coffee".to_string()) {
+       match nodes[0].node.create_offer_builder() {
                Ok(_) => panic!("Expected error"),
                Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths),
        }
@@ -724,7 +789,7 @@ fn fails_creating_refund_without_blinded_paths() {
        let payment_id = PaymentId([1; 32]);
 
        match nodes[0].node.create_refund_builder(
-               "refund".to_string(), 10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
+               10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
        ) {
                Ok(_) => panic!("Expected error"),
                Err(e) => assert_eq!(e, Bolt12SemanticError::MissingPaths),
@@ -747,7 +812,7 @@ fn fails_creating_invoice_request_for_unsupported_chain() {
        let bob = &nodes[1];
 
        let offer = alice.node
-               .create_offer_builder("coffee".to_string()).unwrap()
+               .create_offer_builder().unwrap()
                .clear_chains()
                .chain(Network::Signet)
                .build().unwrap();
@@ -775,9 +840,7 @@ fn fails_sending_invoice_with_unsupported_chain_for_refund() {
        let absolute_expiry = Duration::from_secs(u64::MAX);
        let payment_id = PaymentId([1; 32]);
        let refund = bob.node
-               .create_refund_builder(
-                       "refund".to_string(), 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
-               )
+               .create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
                .unwrap()
                .chain(Network::Signet)
                .build().unwrap();
@@ -809,7 +872,7 @@ fn fails_creating_invoice_request_without_blinded_reply_path() {
        disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
 
        let offer = alice.node
-               .create_offer_builder("coffee".to_string()).unwrap()
+               .create_offer_builder().unwrap()
                .amount_msats(10_000_000)
                .build().unwrap();
 
@@ -843,7 +906,7 @@ fn fails_creating_invoice_request_with_duplicate_payment_id() {
        disconnect_peers(alice, &[charlie, david, &nodes[4], &nodes[5]]);
 
        let offer = alice.node
-               .create_offer_builder("coffee".to_string()).unwrap()
+               .create_offer_builder().unwrap()
                .amount_msats(10_000_000)
                .build().unwrap();
 
@@ -876,13 +939,13 @@ fn fails_creating_refund_with_duplicate_payment_id() {
        let payment_id = PaymentId([1; 32]);
        assert!(
                nodes[0].node.create_refund_builder(
-                       "refund".to_string(), 10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
+                       10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
                ).is_ok()
        );
        expect_recent_payment!(nodes[0], RecentPaymentDetails::AwaitingInvoice, payment_id);
 
        match nodes[0].node.create_refund_builder(
-               "refund".to_string(), 10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
+               10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
        ) {
                Ok(_) => panic!("Expected error"),
                Err(e) => assert_eq!(e, Bolt12SemanticError::DuplicatePaymentId),
@@ -929,7 +992,7 @@ fn fails_sending_invoice_without_blinded_payment_paths_for_offer() {
        disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
 
        let offer = alice.node
-               .create_offer_builder("coffee".to_string()).unwrap()
+               .create_offer_builder().unwrap()
                .amount_msats(10_000_000)
                .build().unwrap();
 
@@ -993,9 +1056,7 @@ fn fails_sending_invoice_without_blinded_payment_paths_for_refund() {
        let absolute_expiry = Duration::from_secs(u64::MAX);
        let payment_id = PaymentId([1; 32]);
        let refund = david.node
-               .create_refund_builder(
-                       "refund".to_string(), 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
-               )
+               .create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
                .unwrap()
                .build().unwrap();
 
@@ -1044,9 +1105,7 @@ fn fails_paying_invoice_more_than_once() {
        let absolute_expiry = Duration::from_secs(u64::MAX);
        let payment_id = PaymentId([1; 32]);
        let refund = david.node
-               .create_refund_builder(
-                       "refund".to_string(), 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
-               )
+               .create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
                .unwrap()
                .build().unwrap();
        expect_recent_payment!(david, RecentPaymentDetails::AwaitingInvoice, payment_id);
@@ -1063,12 +1122,13 @@ fn fails_paying_invoice_more_than_once() {
        david.onion_messenger.handle_onion_message(&charlie_id, &onion_message);
 
        // David pays the first invoice
+       let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {});
        let invoice1 = extract_invoice(david, &onion_message);
 
        route_bolt12_payment(david, &[charlie, bob, alice], &invoice1);
        expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
 
-       claim_bolt12_payment(david, &[charlie, bob, alice]);
+       claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
        expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
 
        disconnect_peers(alice, &[charlie]);
index 00843d5e4e93d0ec11feb609e9d5009c98e85203..f62ca5d84e637bc4d95bb48d679f00151a756d0e 100644 (file)
@@ -11,7 +11,7 @@ use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1};
 use crate::blinded_path;
 use crate::blinded_path::payment::{PaymentConstraints, PaymentRelay};
 use crate::chain::channelmonitor::{HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
-use crate::ln::PaymentHash;
+use crate::ln::types::PaymentHash;
 use crate::ln::channelmanager::{BlindedFailure, BlindedForward, CLTV_FAR_FAR_AWAY, HTLCFailureMsg, MIN_CLTV_EXPIRY_DELTA, PendingHTLCInfo, PendingHTLCRouting};
 use crate::ln::features::BlindedHopFeatures;
 use crate::ln::msgs;
@@ -20,11 +20,13 @@ use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
 use crate::sign::{NodeSigner, Recipient};
 use crate::util::logger::Logger;
 
+#[allow(unused_imports)]
 use crate::prelude::*;
+
 use core::ops::Deref;
 
 /// Invalid inbound onion payment.
-#[derive(Debug)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
 pub struct InboundHTLCErr {
        /// BOLT 4 error code.
        pub err_code: u16,
@@ -129,17 +131,18 @@ pub(super) fn create_recv_pending_htlc_info(
 ) -> Result<PendingHTLCInfo, InboundHTLCErr> {
        let (
                payment_data, keysend_preimage, custom_tlvs, onion_amt_msat, onion_cltv_expiry,
-               payment_metadata, requires_blinded_error
+               payment_metadata, payment_context, requires_blinded_error
        ) = match hop_data {
                msgs::InboundOnionPayload::Receive {
                        payment_data, keysend_preimage, custom_tlvs, sender_intended_htlc_amt_msat,
                        cltv_expiry_height, payment_metadata, ..
                } =>
                        (payment_data, keysend_preimage, custom_tlvs, sender_intended_htlc_amt_msat,
-                        cltv_expiry_height, payment_metadata, false),
+                        cltv_expiry_height, payment_metadata, None, false),
                msgs::InboundOnionPayload::BlindedReceive {
                        sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, payment_secret,
-                       intro_node_blinding_point, payment_constraints, ..
+                       intro_node_blinding_point, payment_constraints, payment_context, keysend_preimage,
+                       custom_tlvs
                } => {
                        check_blinded_payment_constraints(
                                sender_intended_htlc_amt_msat, cltv_expiry, &payment_constraints
@@ -152,8 +155,9 @@ pub(super) fn create_recv_pending_htlc_info(
                                        }
                                })?;
                        let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat };
-                       (Some(payment_data), None, Vec::new(), sender_intended_htlc_amt_msat, cltv_expiry_height,
-                        None, intro_node_blinding_point.is_none())
+                       (Some(payment_data), keysend_preimage, custom_tlvs,
+                        sender_intended_htlc_amt_msat, cltv_expiry_height, None, Some(payment_context),
+                        intro_node_blinding_point.is_none())
                }
                msgs::InboundOnionPayload::Forward { .. } => {
                        return Err(InboundHTLCErr {
@@ -232,11 +236,13 @@ pub(super) fn create_recv_pending_htlc_info(
                        payment_metadata,
                        incoming_cltv_expiry: onion_cltv_expiry,
                        custom_tlvs,
+                       requires_blinded_error,
                }
        } else if let Some(data) = payment_data {
                PendingHTLCRouting::Receive {
                        payment_data: data,
                        payment_metadata,
+                       payment_context,
                        incoming_cltv_expiry: onion_cltv_expiry,
                        phantom_shared_secret,
                        custom_tlvs,
@@ -498,8 +504,7 @@ mod tests {
        use bitcoin::hashes::Hash;
        use bitcoin::hashes::sha256::Hash as Sha256;
        use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
-       use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
-       use crate::ln::ChannelId;
+       use crate::ln::types::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
        use crate::ln::channelmanager::RecipientOnionFields;
        use crate::ln::features::{ChannelFeatures, NodeFeatures};
        use crate::ln::msgs;
@@ -531,7 +536,7 @@ mod tests {
                let path = Path { hops, blinded_tail: None, };
                let onion_keys = super::onion_utils::construct_onion_keys(&secp_ctx, &path, &session_priv).unwrap();
                let (onion_payloads, ..) = super::onion_utils::build_onion_payloads(
-                       &path, total_amt_msat, recipient_onion, cur_height + 1, &Some(keysend_preimage)
+                       &path, total_amt_msat, &recipient_onion, cur_height + 1, &Some(keysend_preimage)
                ).unwrap();
 
                assert!(super::onion_utils::construct_onion_packet(
@@ -558,8 +563,8 @@ mod tests {
                };
 
                let (onion, amount_msat, cltv_expiry) = create_payment_onion(
-                       &secp_ctx, &path, &session_priv, total_amt_msat, recipient_onion, cur_height,
-                       &payment_hash, &Some(preimage), prng_seed
+                       &secp_ctx, &path, &session_priv, total_amt_msat, &recipient_onion,
+                       cur_height, &payment_hash, &Some(preimage), prng_seed
                ).unwrap();
 
                let msg = make_update_add_msg(amount_msat, cltv_expiry, payment_hash, onion);
index 4bdd234f5aeecb214b24fa73b94d145004625c81..c5ce3e051fd42ef3b5bcd82af241fd31a8298d55 100644 (file)
 use crate::chain::channelmonitor::{CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
 use crate::sign::{EntropySource, NodeSigner, Recipient};
 use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentFailureReason};
-use crate::ln::{PaymentHash, PaymentSecret};
+use crate::ln::types::{PaymentHash, PaymentSecret};
 use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
 use crate::ln::channelmanager::{HTLCForwardInfo, FailureCode, CLTV_FAR_FAR_AWAY, DISABLE_GOSSIP_TICKS, MIN_CLTV_EXPIRY_DELTA, PendingAddHTLCInfo, PendingHTLCInfo, PendingHTLCRouting, PaymentId, RecipientOnionFields};
 use crate::ln::onion_utils;
 use crate::routing::gossip::{NetworkUpdate, RoutingFees};
 use crate::routing::router::{get_route, PaymentParameters, Route, RouteParameters, RouteHint, RouteHintHop};
 use crate::ln::features::{InitFeatures, Bolt11InvoiceFeatures};
+use crate::ln::functional_test_utils::test_default_channel_config;
 use crate::ln::msgs;
-use crate::ln::msgs::{ChannelMessageHandler, ChannelUpdate};
+use crate::ln::msgs::{ChannelMessageHandler, ChannelUpdate, OutboundTrampolinePayload};
 use crate::ln::wire::Encode;
 use crate::util::ser::{Writeable, Writer, BigSize};
 use crate::util::test_utils;
@@ -35,11 +36,11 @@ use bitcoin::hashes::hmac::{Hmac, HmacEngine};
 use bitcoin::hashes::sha256::Hash as Sha256;
 
 use bitcoin::secp256k1;
-use bitcoin::secp256k1::{Secp256k1, SecretKey};
+use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
 
 use crate::io;
 use crate::prelude::*;
-use core::default::Default;
+use bitcoin::hashes::hex::FromHex;
 
 use crate::ln::functional_test_utils::*;
 
@@ -328,7 +329,7 @@ fn test_onion_failure() {
        // to 2000, which is above the default value of 1000 set in create_node_chanmgrs.
        // This exposed a previous bug because we were using the wrong value all the way down in
        // Channel::get_counterparty_htlc_minimum_msat().
-       let mut node_2_cfg: UserConfig = Default::default();
+       let mut node_2_cfg: UserConfig = test_default_channel_config();
        node_2_cfg.channel_handshake_config.our_htlc_minimum_msat = 2000;
        node_2_cfg.channel_handshake_config.announced_channel = true;
        node_2_cfg.channel_handshake_limits.force_announced_channel_preference = false;
@@ -356,8 +357,9 @@ fn test_onion_failure() {
                let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
                let cur_height = nodes[0].best_block_info().1 + 1;
                let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
+               let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
                let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(
-                       &route.paths[0], 40000, RecipientOnionFields::spontaneous_empty(), cur_height, &None).unwrap();
+                       &route.paths[0], 40000, &recipient_onion_fields, cur_height, &None).unwrap();
                let mut new_payloads = Vec::new();
                for payload in onion_payloads.drain(..) {
                        new_payloads.push(BogusOnionHopData::new(payload));
@@ -374,8 +376,9 @@ fn test_onion_failure() {
                let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
                let cur_height = nodes[0].best_block_info().1 + 1;
                let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
+               let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
                let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(
-                       &route.paths[0], 40000, RecipientOnionFields::spontaneous_empty(), cur_height, &None).unwrap();
+                       &route.paths[0], 40000, &recipient_onion_fields, cur_height, &None).unwrap();
                let mut new_payloads = Vec::new();
                for payload in onion_payloads.drain(..) {
                        new_payloads.push(BogusOnionHopData::new(payload));
@@ -611,8 +614,9 @@ fn test_onion_failure() {
                let height = nodes[2].best_block_info().1;
                route.paths[0].hops[1].cltv_expiry_delta += CLTV_FAR_FAR_AWAY + route.paths[0].hops[0].cltv_expiry_delta + 1;
                let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
+               let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
                let (onion_payloads, _, htlc_cltv) = onion_utils::build_onion_payloads(
-                       &route.paths[0], 40000, RecipientOnionFields::spontaneous_empty(), height, &None).unwrap();
+                       &route.paths[0], 40000, &recipient_onion_fields, height, &None).unwrap();
                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;
@@ -947,8 +951,9 @@ fn test_always_create_tlv_format_onion_payloads() {
        assert!(!hops[1].node_features.supports_variable_length_onion());
 
        let cur_height = nodes[0].best_block_info().1 + 1;
+       let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
        let (onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(
-               &route.paths[0], 40000, RecipientOnionFields::spontaneous_empty(), cur_height, &None).unwrap();
+               &route.paths[0], 40000, &recipient_onion_fields, cur_height, &None).unwrap();
 
        match onion_payloads[0] {
                msgs::OutboundOnionPayload::Forward {..} => {},
@@ -966,6 +971,25 @@ fn test_always_create_tlv_format_onion_payloads() {
        }
 }
 
+#[test]
+fn test_trampoline_onion_payload_serialization() {
+       // As per https://github.com/lightning/bolts/blob/c01d2e6267d4a8d1095f0f1188970055a9a22d29/bolt04/trampoline-payment-onion-test.json#L3
+       let trampoline_payload = OutboundTrampolinePayload::Forward {
+               amt_to_forward: 100000000,
+               outgoing_cltv_value: 800000,
+               outgoing_node_id: PublicKey::from_slice(&<Vec<u8>>::from_hex("02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145").unwrap()).unwrap(),
+       };
+
+       let slice_to_hex = |slice: &[u8]| {
+               slice.iter()
+                       .map(|b| format!("{:02x}", b).to_string())
+                       .collect::<String>()
+       };
+
+       let carol_payload_hex = slice_to_hex(&trampoline_payload.encode());
+       assert_eq!(carol_payload_hex, "2e020405f5e10004030c35000e2102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145");
+}
+
 fn do_test_fail_htlc_backwards_with_reason(failure_code: FailureCode) {
 
        let chanmon_cfgs = create_chanmon_cfgs(2);
@@ -1183,9 +1207,10 @@ fn test_phantom_invalid_onion_payload() {
                                        let height = nodes[0].best_block_info().1;
                                        let session_priv = SecretKey::from_slice(&session_priv).unwrap();
                                        let mut onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
+                                       let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret);
                                        let (mut onion_payloads, _, _) = onion_utils::build_onion_payloads(
                                                &route.paths[0], msgs::MAX_VALUE_MSAT + 1,
-                                               RecipientOnionFields::secret_only(payment_secret), height + 1, &None).unwrap();
+                                               &recipient_onion_fields, height + 1, &None).unwrap();
                                        // We only want to construct the onion packet for the last hop, not the entire route, so
                                        // remove the first hop's payload and its keys.
                                        onion_keys.remove(0);
index c705c4afda8897d17dc2e81b50c27981cfd80a4b..9a207c9e52d1dd1805592bf039dd9ff84387a6b4 100644 (file)
@@ -7,14 +7,17 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
+use crate::blinded_path::BlindedHop;
 use crate::crypto::chacha20::ChaCha20;
 use crate::crypto::streams::ChaChaReader;
+use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
 use crate::ln::channelmanager::{HTLCSource, RecipientOnionFields};
+use crate::ln::features::{ChannelFeatures, NodeFeatures};
 use crate::ln::msgs;
+use crate::ln::types::{PaymentHash, PaymentPreimage};
 use crate::ln::wire::Encode;
-use crate::ln::{PaymentHash, PaymentPreimage};
 use crate::routing::gossip::NetworkUpdate;
-use crate::routing::router::{BlindedTail, Path, RouteHop};
+use crate::routing::router::{Path, RouteHop, RouteParameters};
 use crate::sign::NodeSigner;
 use crate::util::errors::{self, APIError};
 use crate::util::logger::Logger;
@@ -30,10 +33,11 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
 use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
 
 use crate::io::{Cursor, Read};
-use crate::prelude::*;
-use core::convert::{AsMut, TryInto};
 use core::ops::Deref;
 
+#[allow(unused_imports)]
+use crate::prelude::*;
+
 pub(crate) struct OnionKeys {
        #[cfg(test)]
        pub(crate) shared_secret: SharedSecret,
@@ -173,18 +177,60 @@ pub(super) fn construct_onion_keys<T: secp256k1::Signing>(
 }
 
 /// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
-pub(super) fn build_onion_payloads(
-       path: &Path, total_msat: u64, mut recipient_onion: RecipientOnionFields,
+pub(super) fn build_onion_payloads<'a>(
+       path: &'a Path, total_msat: u64, recipient_onion: &'a RecipientOnionFields,
        starting_htlc_offset: u32, keysend_preimage: &Option<PaymentPreimage>,
-) -> Result<(Vec<msgs::OutboundOnionPayload>, u64, u32), APIError> {
-       let mut cur_value_msat = 0u64;
-       let mut cur_cltv = starting_htlc_offset;
-       let mut last_short_channel_id = 0;
+) -> Result<(Vec<msgs::OutboundOnionPayload<'a>>, u64, u32), APIError> {
        let mut res: Vec<msgs::OutboundOnionPayload> = Vec::with_capacity(
                path.hops.len() + path.blinded_tail.as_ref().map_or(0, |t| t.hops.len()),
        );
+       let blinded_tail_with_hop_iter = path.blinded_tail.as_ref().map(|bt| BlindedTailHopIter {
+               hops: bt.hops.iter(),
+               blinding_point: bt.blinding_point,
+               final_value_msat: bt.final_value_msat,
+               excess_final_cltv_expiry_delta: bt.excess_final_cltv_expiry_delta,
+       });
+
+       let (value_msat, cltv) = build_onion_payloads_callback(
+               path.hops.iter(),
+               blinded_tail_with_hop_iter,
+               total_msat,
+               recipient_onion,
+               starting_htlc_offset,
+               keysend_preimage,
+               |action, payload| match action {
+                       PayloadCallbackAction::PushBack => res.push(payload),
+                       PayloadCallbackAction::PushFront => res.insert(0, payload),
+               },
+       )?;
+       Ok((res, value_msat, cltv))
+}
+
+struct BlindedTailHopIter<'a, I: Iterator<Item = &'a BlindedHop>> {
+       hops: I,
+       blinding_point: PublicKey,
+       final_value_msat: u64,
+       excess_final_cltv_expiry_delta: u32,
+}
+enum PayloadCallbackAction {
+       PushBack,
+       PushFront,
+}
+fn build_onion_payloads_callback<'a, H, B, F>(
+       hops: H, mut blinded_tail: Option<BlindedTailHopIter<'a, B>>, total_msat: u64,
+       recipient_onion: &'a RecipientOnionFields, starting_htlc_offset: u32,
+       keysend_preimage: &Option<PaymentPreimage>, mut callback: F,
+) -> Result<(u64, u32), APIError>
+where
+       H: DoubleEndedIterator<Item = &'a RouteHop>,
+       B: ExactSizeIterator<Item = &'a BlindedHop>,
+       F: FnMut(PayloadCallbackAction, msgs::OutboundOnionPayload<'a>),
+{
+       let mut cur_value_msat = 0u64;
+       let mut cur_cltv = starting_htlc_offset;
+       let mut last_short_channel_id = 0;
 
-       for (idx, hop) in path.hops.iter().rev().enumerate() {
+       for (idx, hop) in hops.rev().enumerate() {
                // First hop gets special values so that it can check, on receipt, that everything is
                // exactly as it should be (and the next hop isn't trying to probe to find out if we're
                // the intended recipient).
@@ -195,45 +241,55 @@ pub(super) fn build_onion_payloads(
                        cur_cltv
                };
                if idx == 0 {
-                       if let Some(BlindedTail {
+                       if let Some(BlindedTailHopIter {
                                blinding_point,
                                hops,
                                final_value_msat,
                                excess_final_cltv_expiry_delta,
                                ..
-                       }) = &path.blinded_tail
+                       }) = blinded_tail.take()
                        {
-                               let mut blinding_point = Some(*blinding_point);
-                               for (i, blinded_hop) in hops.iter().enumerate() {
-                                       if i == hops.len() - 1 {
+                               let mut blinding_point = Some(blinding_point);
+                               let hops_len = hops.len();
+                               for (i, blinded_hop) in hops.enumerate() {
+                                       if i == hops_len - 1 {
                                                cur_value_msat += final_value_msat;
-                                               res.push(msgs::OutboundOnionPayload::BlindedReceive {
-                                                       sender_intended_htlc_amt_msat: *final_value_msat,
-                                                       total_msat,
-                                                       cltv_expiry_height: cur_cltv + excess_final_cltv_expiry_delta,
-                                                       encrypted_tlvs: blinded_hop.encrypted_payload.clone(),
-                                                       intro_node_blinding_point: blinding_point.take(),
-                                               });
+                                               callback(
+                                                       PayloadCallbackAction::PushBack,
+                                                       msgs::OutboundOnionPayload::BlindedReceive {
+                                                               sender_intended_htlc_amt_msat: final_value_msat,
+                                                               total_msat,
+                                                               cltv_expiry_height: cur_cltv + excess_final_cltv_expiry_delta,
+                                                               encrypted_tlvs: &blinded_hop.encrypted_payload,
+                                                               intro_node_blinding_point: blinding_point.take(),
+                                                               keysend_preimage: *keysend_preimage,
+                                                               custom_tlvs: &recipient_onion.custom_tlvs,
+                                                       },
+                                               );
                                        } else {
-                                               res.push(msgs::OutboundOnionPayload::BlindedForward {
-                                                       encrypted_tlvs: blinded_hop.encrypted_payload.clone(),
-                                                       intro_node_blinding_point: blinding_point.take(),
-                                               });
+                                               callback(
+                                                       PayloadCallbackAction::PushBack,
+                                                       msgs::OutboundOnionPayload::BlindedForward {
+                                                               encrypted_tlvs: &blinded_hop.encrypted_payload,
+                                                               intro_node_blinding_point: blinding_point.take(),
+                                                       },
+                                               );
                                        }
                                }
                        } else {
-                               res.push(msgs::OutboundOnionPayload::Receive {
-                                       payment_data: if let Some(secret) = recipient_onion.payment_secret.take() {
-                                               Some(msgs::FinalOnionHopData { payment_secret: secret, total_msat })
-                                       } else {
-                                               None
+                               callback(
+                                       PayloadCallbackAction::PushBack,
+                                       msgs::OutboundOnionPayload::Receive {
+                                               payment_data: recipient_onion.payment_secret.map(|payment_secret| {
+                                                       msgs::FinalOnionHopData { payment_secret, total_msat }
+                                               }),
+                                               payment_metadata: recipient_onion.payment_metadata.as_ref(),
+                                               keysend_preimage: *keysend_preimage,
+                                               custom_tlvs: &recipient_onion.custom_tlvs,
+                                               sender_intended_htlc_amt_msat: value_msat,
+                                               cltv_expiry_height: cltv,
                                        },
-                                       payment_metadata: recipient_onion.payment_metadata.take(),
-                                       keysend_preimage: *keysend_preimage,
-                                       custom_tlvs: recipient_onion.custom_tlvs.clone(),
-                                       sender_intended_htlc_amt_msat: value_msat,
-                                       cltv_expiry_height: cltv,
-                               });
+                               );
                        }
                } else {
                        let payload = msgs::OutboundOnionPayload::Forward {
@@ -241,7 +297,7 @@ pub(super) fn build_onion_payloads(
                                amt_to_forward: value_msat,
                                outgoing_cltv_value: cltv,
                        };
-                       res.insert(0, payload);
+                       callback(PayloadCallbackAction::PushFront, payload);
                }
                cur_value_msat += hop.fee_msat;
                if cur_value_msat >= 21000000 * 100000000 * 1000 {
@@ -253,7 +309,78 @@ pub(super) fn build_onion_payloads(
                }
                last_short_channel_id = hop.short_channel_id;
        }
-       Ok((res, cur_value_msat, cur_cltv))
+       Ok((cur_value_msat, cur_cltv))
+}
+
+pub(crate) const MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY: u64 = 100_000_000;
+
+pub(crate) fn set_max_path_length(
+       route_params: &mut RouteParameters, recipient_onion: &RecipientOnionFields,
+       keysend_preimage: Option<PaymentPreimage>, best_block_height: u32,
+) -> Result<(), ()> {
+       const PAYLOAD_HMAC_LEN: usize = 32;
+       let unblinded_intermed_payload_len = msgs::OutboundOnionPayload::Forward {
+               short_channel_id: 42,
+               amt_to_forward: TOTAL_BITCOIN_SUPPLY_SATOSHIS,
+               outgoing_cltv_value: route_params.payment_params.max_total_cltv_expiry_delta,
+       }
+       .serialized_length()
+       .saturating_add(PAYLOAD_HMAC_LEN);
+
+       const OVERPAY_ESTIMATE_MULTIPLER: u64 = 3;
+       let final_value_msat_with_overpay_buffer = core::cmp::max(
+               route_params.final_value_msat.saturating_mul(OVERPAY_ESTIMATE_MULTIPLER),
+               MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY,
+       );
+
+       let blinded_tail_opt = route_params
+               .payment_params
+               .payee
+               .blinded_route_hints()
+               .iter()
+               .map(|(_, path)| path)
+               .max_by_key(|path| path.serialized_length())
+               .map(|largest_path| BlindedTailHopIter {
+                       hops: largest_path.blinded_hops.iter(),
+                       blinding_point: largest_path.blinding_point,
+                       final_value_msat: final_value_msat_with_overpay_buffer,
+                       excess_final_cltv_expiry_delta: 0,
+               });
+
+       let unblinded_route_hop = RouteHop {
+               pubkey: PublicKey::from_slice(&[2; 33]).unwrap(),
+               node_features: NodeFeatures::empty(),
+               short_channel_id: 42,
+               channel_features: ChannelFeatures::empty(),
+               fee_msat: final_value_msat_with_overpay_buffer,
+               cltv_expiry_delta: route_params.payment_params.max_total_cltv_expiry_delta,
+               maybe_announced_channel: false,
+       };
+       let mut num_reserved_bytes: usize = 0;
+       let build_payloads_res = build_onion_payloads_callback(
+               core::iter::once(&unblinded_route_hop),
+               blinded_tail_opt,
+               final_value_msat_with_overpay_buffer,
+               &recipient_onion,
+               best_block_height,
+               &keysend_preimage,
+               |_, payload| {
+                       num_reserved_bytes = num_reserved_bytes
+                               .saturating_add(payload.serialized_length())
+                               .saturating_add(PAYLOAD_HMAC_LEN);
+               },
+       );
+       debug_assert!(build_payloads_res.is_ok());
+
+       let max_path_length = 1300usize
+               .checked_sub(num_reserved_bytes)
+               .map(|p| p / unblinded_intermed_payload_len)
+               .and_then(|l| u8::try_from(l.saturating_add(1)).ok())
+               .ok_or(())?;
+
+       route_params.payment_params.max_path_length =
+               core::cmp::min(max_path_length, route_params.payment_params.max_path_length);
+       Ok(())
 }
 
 /// Length of the onion data packet. Before TLV-based onions this was 20 65-byte hops, though now
@@ -290,6 +417,24 @@ pub(super) fn construct_onion_packet(
        )
 }
 
+#[allow(unused)]
+pub(super) fn construct_trampoline_onion_packet(
+       payloads: Vec<msgs::OutboundTrampolinePayload>, onion_keys: Vec<OnionKeys>,
+       prng_seed: [u8; 32], associated_data: &PaymentHash, length: u16,
+) -> Result<msgs::TrampolineOnionPacket, ()> {
+       let mut packet_data = vec![0u8; length as usize];
+
+       let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]);
+       chacha.process(&vec![0u8; length as usize], &mut packet_data);
+
+       construct_onion_packet_with_init_noise::<_, _>(
+               payloads,
+               onion_keys,
+               packet_data,
+               Some(associated_data),
+       )
+}
+
 #[cfg(test)]
 /// Used in testing to write bogus `BogusOnionHopData` as well as `RawOnionHopData`, which is
 /// otherwise not representable in `msgs::OnionHopData`.
@@ -1053,6 +1198,21 @@ pub(crate) enum Hop {
        },
 }
 
+impl Hop {
+       pub(crate) fn is_intro_node_blinded_forward(&self) -> bool {
+               match self {
+                       Self::Forward {
+                               next_hop_data:
+                                       msgs::InboundOnionPayload::BlindedForward {
+                                               intro_node_blinding_point: Some(_), ..
+                                       },
+                               ..
+                       } => true,
+                       _ => false,
+               }
+       }
+}
+
 /// Error returned when we fail to decode the onion packet.
 #[derive(Debug)]
 pub(crate) enum OnionDecodeErr {
@@ -1088,7 +1248,7 @@ where
 /// `cur_block_height` should be set to the best known block height + 1.
 pub fn create_payment_onion<T: secp256k1::Signing>(
        secp_ctx: &Secp256k1<T>, path: &Path, session_priv: &SecretKey, total_msat: u64,
-       recipient_onion: RecipientOnionFields, cur_block_height: u32, payment_hash: &PaymentHash,
+       recipient_onion: &RecipientOnionFields, cur_block_height: u32, payment_hash: &PaymentHash,
        keysend_preimage: &Option<PaymentPreimage>, prng_seed: [u8; 32],
 ) -> Result<(msgs::OnionPacket, u64, u32), APIError> {
        let onion_keys = construct_onion_keys(&secp_ctx, &path, &session_priv).map_err(|_| {
@@ -1204,11 +1364,13 @@ mod tests {
        use crate::io;
        use crate::ln::features::{ChannelFeatures, NodeFeatures};
        use crate::ln::msgs;
-       use crate::ln::PaymentHash;
-       use crate::prelude::*;
+       use crate::ln::types::PaymentHash;
        use crate::routing::router::{Path, Route, RouteHop};
        use crate::util::ser::{VecWriter, Writeable, Writer};
 
+       #[allow(unused_imports)]
+       use crate::prelude::*;
+
        use bitcoin::hashes::hex::FromHex;
        use bitcoin::secp256k1::Secp256k1;
        use bitcoin::secp256k1::{PublicKey, SecretKey};
index b05d6f3f7290110b5aed103faef370203350a8fc..d615c447e65295d4fe9e678b32ad007f1b46036c 100644 (file)
@@ -15,8 +15,9 @@ use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
 
 use crate::sign::{EntropySource, NodeSigner, Recipient};
 use crate::events::{self, PaymentFailureReason};
-use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
+use crate::ln::types::{PaymentHash, PaymentPreimage, PaymentSecret};
 use crate::ln::channelmanager::{ChannelDetails, EventCompletionAction, HTLCSource, PaymentId};
+use crate::ln::onion_utils;
 use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
 use crate::offers::invoice::Bolt12Invoice;
 use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
@@ -421,6 +422,12 @@ pub enum RetryableSendFailure {
        /// [`Event::PaymentSent`]: crate::events::Event::PaymentSent
        /// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
        DuplicatePayment,
+       /// The [`RecipientOnionFields::payment_metadata`], [`RecipientOnionFields::custom_tlvs`], or
+       /// [`BlindedPath`]s provided are too large and caused us to exceed the maximum onion packet size
+       /// of 1300 bytes.
+       ///
+       /// [`BlindedPath`]: crate::blinded_path::BlindedPath
+       OnionPacketSizeExceeded,
 }
 
 /// If a payment fails to send with [`ChannelManager::send_payment_with_route`], it can be in one
@@ -659,7 +666,7 @@ impl RecipientOnionFields {
 pub(super) struct SendAlongPathArgs<'a> {
        pub path: &'a Path,
        pub payment_hash: &'a PaymentHash,
-       pub recipient_onion: RecipientOnionFields,
+       pub recipient_onion: &'a RecipientOnionFields,
        pub total_value: u64,
        pub cur_height: u32,
        pub payment_id: PaymentId,
@@ -711,7 +718,7 @@ impl OutboundPayments {
                F: Fn(SendAlongPathArgs) -> Result<(), APIError>
        {
                let onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(), payment_id, None, route, None, None, entropy_source, best_block_height)?;
-               self.pay_route_internal(route, payment_hash, recipient_onion, None, payment_id, None,
+               self.pay_route_internal(route, payment_hash, &recipient_onion, None, payment_id, None,
                        onion_session_privs, node_signer, best_block_height, &send_payment_along_path)
                        .map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
        }
@@ -756,7 +763,7 @@ impl OutboundPayments {
                let onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(),
                        payment_id, Some(preimage), &route, None, None, entropy_source, best_block_height)?;
 
-               match self.pay_route_internal(route, payment_hash, recipient_onion, Some(preimage),
+               match self.pay_route_internal(route, payment_hash, &recipient_onion, Some(preimage),
                        payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path
                ) {
                        Ok(()) => Ok(payment_hash),
@@ -886,7 +893,7 @@ impl OutboundPayments {
        /// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
        fn send_payment_internal<R: Deref, NS: Deref, ES: Deref, IH, SP, L: Deref>(
                &self, payment_id: PaymentId, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
-               keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, route_params: RouteParameters,
+               keysend_preimage: Option<PaymentPreimage>, retry_strategy: Retry, 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<VecDeque<(events::Event, Option<EventCompletionAction>)>>, send_payment_along_path: SP,
@@ -907,6 +914,15 @@ impl OutboundPayments {
                        }
                }
 
+               onion_utils::set_max_path_length(
+                       &mut route_params, &recipient_onion, keysend_preimage, best_block_height
+               )
+                       .map_err(|()| {
+                               log_error!(logger, "Can't construct an onion packet without exceeding 1300-byte onion \
+                                       hop_data length for payment with id {} and hash {}", payment_id, payment_hash);
+                               RetryableSendFailure::OnionPacketSizeExceeded
+                       })?;
+
                let mut route = router.find_route_with_id(
                        &node_signer.get_node_id(Recipient::Node).unwrap(), &route_params,
                        Some(&first_hops.iter().collect::<Vec<_>>()), inflight_htlcs(),
@@ -932,8 +948,9 @@ impl OutboundPayments {
                                RetryableSendFailure::DuplicatePayment
                        })?;
 
-               let res = self.pay_route_internal(&route, payment_hash, recipient_onion, keysend_preimage, payment_id, None,
-                       onion_session_privs, node_signer, best_block_height, &send_payment_along_path);
+               let res = self.pay_route_internal(&route, payment_hash, &recipient_onion,
+                       keysend_preimage, payment_id, None, onion_session_privs, node_signer,
+                       best_block_height, &send_payment_along_path);
                log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
                        payment_id, payment_hash, res);
                if let Err(e) = res {
@@ -1090,7 +1107,7 @@ impl OutboundPayments {
                                }
                        }
                };
-               let res = self.pay_route_internal(&route, payment_hash, recipient_onion, keysend_preimage,
+               let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
                        payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height,
                        &send_payment_along_path);
                log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
@@ -1201,7 +1218,8 @@ impl OutboundPayments {
                        RecipientOnionFields::secret_only(payment_secret), payment_id, None, &route, None, None,
                        entropy_source, best_block_height)?;
 
-               match self.pay_route_internal(&route, payment_hash, RecipientOnionFields::spontaneous_empty(),
+               let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
+               match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields,
                        None, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path
                ) {
                        Ok(()) => Ok((payment_hash, payment_id)),
@@ -1309,7 +1327,7 @@ impl OutboundPayments {
        }
 
        fn pay_route_internal<NS: Deref, F>(
-               &self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
+               &self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
                keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>,
                onion_session_privs: Vec<[u8; 32]>, node_signer: &NS, best_block_height: u32,
                send_payment_along_path: &F
@@ -1364,8 +1382,9 @@ impl OutboundPayments {
                debug_assert_eq!(route.paths.len(), onion_session_privs.len());
                for (path, session_priv_bytes) in route.paths.iter().zip(onion_session_privs.into_iter()) {
                        let mut path_res = send_payment_along_path(SendAlongPathArgs {
-                               path: &path, payment_hash: &payment_hash, recipient_onion: recipient_onion.clone(),
-                               total_value, cur_height, payment_id, keysend_preimage: &keysend_preimage, session_priv_bytes
+                               path: &path, payment_hash: &payment_hash, recipient_onion, total_value,
+                               cur_height, payment_id, keysend_preimage: &keysend_preimage,
+                               session_priv_bytes
                        });
                        match path_res {
                                Ok(_) => {},
@@ -1448,9 +1467,9 @@ impl OutboundPayments {
                NS::Target: NodeSigner,
                F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
        {
-               self.pay_route_internal(route, payment_hash, recipient_onion, keysend_preimage, payment_id,
-                       recv_value_msat, onion_session_privs, node_signer, best_block_height,
-                       &send_payment_along_path)
+               self.pay_route_internal(route, payment_hash, &recipient_onion,
+                       keysend_preimage, payment_id, recv_value_msat, onion_session_privs,
+                       node_signer, best_block_height, &send_payment_along_path)
                        .map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
        }
 
@@ -1831,13 +1850,13 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
 
 #[cfg(test)]
 mod tests {
-       use bitcoin::network::constants::Network;
+       use bitcoin::network::Network;
        use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
 
        use core::time::Duration;
 
        use crate::events::{Event, PathFailure, PaymentFailureReason};
-       use crate::ln::PaymentHash;
+       use crate::ln::types::PaymentHash;
        use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
        use crate::ln::features::{ChannelFeatures, NodeFeatures};
        use crate::ln::msgs::{ErrorAction, LightningError};
@@ -2194,7 +2213,7 @@ mod tests {
                assert!(outbound_payments.has_pending_payments());
 
                let created_at = now() - DEFAULT_RELATIVE_EXPIRY;
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2237,7 +2256,7 @@ mod tests {
                let payment_id = PaymentId([0; 32]);
                let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
 
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2296,7 +2315,7 @@ mod tests {
                let payment_id = PaymentId([0; 32]);
                let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
 
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
index 09c022a4c87b1f3e8d106126fde4fd212c22b75a..60823845efb6908daa205214542f18d6701367f1 100644 (file)
 //! serialization ordering between ChannelManager/ChannelMonitors and ensuring we can still retry
 //! payments thereafter.
 
-use crate::chain::{ChannelMonitorUpdateStatus, Confirm, Listen, Watch};
+use crate::chain::{ChannelMonitorUpdateStatus, Confirm, Listen};
 use crate::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
 use crate::sign::EntropySource;
-use crate::chain::transaction::OutPoint;
 use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentFailureReason, PaymentPurpose};
 use crate::ln::channel::{EXPIRE_PREV_CONFIG_TICKS, commit_tx_fee_msat, get_holder_selected_channel_reserve_satoshis, ANCHOR_OUTPUT_VALUE_SATOSHI};
 use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, RecentPaymentDetails, RecipientOnionFields, HTLCForwardInfo, PendingHTLCRouting, PendingAddHTLCInfo};
 use crate::ln::features::{Bolt11InvoiceFeatures, ChannelTypeFeatures};
-use crate::ln::{msgs, ChannelId, PaymentHash, PaymentSecret, PaymentPreimage};
+use crate::ln::msgs;
+use crate::ln::types::{ChannelId, PaymentHash, PaymentSecret, PaymentPreimage};
 use crate::ln::msgs::ChannelMessageHandler;
 use crate::ln::onion_utils;
 use crate::ln::outbound_payment::{IDEMPOTENCY_TIMEOUT_TICKS, Retry};
@@ -34,7 +34,7 @@ use crate::util::string::UntrustedString;
 
 use bitcoin::hashes::Hash;
 use bitcoin::hashes::sha256::Hash as Sha256;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 use bitcoin::secp256k1::{Secp256k1, SecretKey};
 
 use crate::prelude::*;
@@ -160,7 +160,9 @@ fn mpp_retry() {
        let mut events = nodes[0].node.get_and_clear_pending_msg_events();
        assert_eq!(events.len(), 1);
        pass_along_path(&nodes[0], &[&nodes[2], &nodes[3]], 2_000_000, payment_hash, Some(payment_secret), events.pop().unwrap(), true, None);
-       claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], payment_preimage)
+       );
 }
 
 #[test]
@@ -351,7 +353,9 @@ fn do_mpp_receive_timeout(send_partial_mpp: bool) {
                        nodes[3].node.timer_tick_occurred();
                }
 
-               claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage);
+               claim_payment_along_route(
+                       ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], payment_preimage)
+               );
        }
 }
 
@@ -466,7 +470,9 @@ fn test_mpp_keysend() {
        let ev = remove_first_msg_event_to_node(&nodes[2].node.get_our_node_id(), &mut events);
        pass_along_path(&nodes[0], expected_route[1], recv_value, payment_hash.clone(),
                Some(payment_secret), ev.clone(), true, Some(payment_preimage));
-       claim_payment_along_route(&nodes[0], expected_route, false, payment_preimage);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], expected_route, payment_preimage)
+       );
 }
 
 #[test]
@@ -817,7 +823,9 @@ fn do_retry_with_no_persist(confirm_before_reload: bool) {
        let mut events = nodes[0].node.get_and_clear_pending_msg_events();
        assert_eq!(events.len(), 1);
        pass_along_path(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000, payment_hash, Some(payment_secret), events.pop().unwrap(), true, None);
-       do_claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], false, payment_preimage);
+       do_claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage)
+       );
        expect_payment_sent!(nodes[0], payment_preimage, Some(new_route.paths[0].hops[0].fee_msat));
 }
 
@@ -1030,16 +1038,15 @@ fn test_completed_payment_not_retryable_on_reload() {
        do_test_completed_payment_not_retryable_on_reload(false);
 }
 
-
-fn do_test_dup_htlc_onchain_fails_on_reload(persist_manager_post_event: bool, confirm_commitment_tx: bool, payment_timeout: bool) {
+fn do_test_dup_htlc_onchain_doesnt_fail_on_reload(persist_manager_post_event: bool, confirm_commitment_tx: bool, payment_timeout: bool) {
        // When a Channel is closed, any outbound HTLCs which were relayed through it are simply
-       // dropped when the Channel is. From there, the ChannelManager relies on the ChannelMonitor
-       // having a copy of the relevant fail-/claim-back data and processes the HTLC fail/claim when
-       // the ChannelMonitor tells it to.
+       // dropped. From there, the ChannelManager relies on the ChannelMonitor having a copy of the
+       // relevant fail-/claim-back data and processes the HTLC fail/claim when the ChannelMonitor tells
+       // it to.
        //
-       // If, due to an on-chain event, an HTLC is failed/claimed, we should avoid providing the
-       // ChannelManager the HTLC event until after the monitor is re-persisted. This should prevent a
-       // duplicate HTLC fail/claim (e.g. via a PaymentPathFailed event).
+       // If, due to an on-chain event, an HTLC is failed/claimed, we provide the
+       // ChannelManager with the HTLC event without waiting for ChannelMonitor persistence.
+       // This might generate duplicate HTLC fail/claim (e.g. via a PaymentPathFailed event) on reload.
        let chanmon_cfgs = create_chanmon_cfgs(2);
        let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
        let persister;
@@ -1103,7 +1110,6 @@ fn do_test_dup_htlc_onchain_fails_on_reload(persist_manager_post_event: bool, co
        // Now connect the HTLC claim transaction with the ChainMonitor-generated ChannelMonitor update
        // returning InProgress. This should cause the claim event to never make its way to the
        // ChannelManager.
-       chanmon_cfgs[0].persister.chain_sync_monitor_persistences.lock().unwrap().clear();
        chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
 
        if payment_timeout {
@@ -1112,14 +1118,9 @@ fn do_test_dup_htlc_onchain_fails_on_reload(persist_manager_post_event: bool, co
                connect_block(&nodes[0], &claim_block);
        }
 
-       let funding_txo = OutPoint { txid: funding_tx.txid(), index: 0 };
-       let mon_updates: Vec<_> = chanmon_cfgs[0].persister.chain_sync_monitor_persistences.lock().unwrap()
-               .get_mut(&funding_txo).unwrap().drain().collect();
-       // If we are using chain::Confirm instead of chain::Listen, we will get the same update twice.
-       // If we're testing connection idempotency we may get substantially more.
-       assert!(mon_updates.len() >= 1);
-       assert!(nodes[0].chain_monitor.release_pending_monitor_events().is_empty());
-       assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
+       // Note that we skip persisting ChannelMonitors. We should still be generating the payment sent
+       // event without ChannelMonitor persistence. If we reset to a previous state on reload, the block
+       // should be replayed and we'll regenerate the event.
 
        // If we persist the ChannelManager here, we should get the PaymentSent event after
        // deserialization.
@@ -1128,13 +1129,7 @@ fn do_test_dup_htlc_onchain_fails_on_reload(persist_manager_post_event: bool, co
                chan_manager_serialized = nodes[0].node.encode();
        }
 
-       // Now persist the ChannelMonitor and inform the ChainMonitor that we're done, generating the
-       // payment sent event.
-       chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed);
        let chan_0_monitor_serialized = get_monitor!(nodes[0], chan_id).encode();
-       for update in mon_updates {
-               nodes[0].chain_monitor.chain_monitor.channel_monitor_updated(funding_txo, update).unwrap();
-       }
        if payment_timeout {
                expect_payment_failed!(nodes[0], payment_hash, false);
        } else {
@@ -1168,13 +1163,13 @@ fn do_test_dup_htlc_onchain_fails_on_reload(persist_manager_post_event: bool, co
 }
 
 #[test]
-fn test_dup_htlc_onchain_fails_on_reload() {
-       do_test_dup_htlc_onchain_fails_on_reload(true, true, true);
-       do_test_dup_htlc_onchain_fails_on_reload(true, true, false);
-       do_test_dup_htlc_onchain_fails_on_reload(true, false, false);
-       do_test_dup_htlc_onchain_fails_on_reload(false, true, true);
-       do_test_dup_htlc_onchain_fails_on_reload(false, true, false);
-       do_test_dup_htlc_onchain_fails_on_reload(false, false, false);
+fn test_dup_htlc_onchain_doesnt_fail_on_reload() {
+       do_test_dup_htlc_onchain_doesnt_fail_on_reload(true, true, true);
+       do_test_dup_htlc_onchain_doesnt_fail_on_reload(true, true, false);
+       do_test_dup_htlc_onchain_doesnt_fail_on_reload(true, false, false);
+       do_test_dup_htlc_onchain_doesnt_fail_on_reload(false, true, true);
+       do_test_dup_htlc_onchain_doesnt_fail_on_reload(false, true, false);
+       do_test_dup_htlc_onchain_doesnt_fail_on_reload(false, false, false);
 }
 
 #[test]
@@ -1258,7 +1253,9 @@ fn get_ldk_payment_preimage() {
        let mut events = nodes[0].node.get_and_clear_pending_msg_events();
        assert_eq!(events.len(), 1);
        pass_along_path(&nodes[0], &[&nodes[1]], amt_msat, payment_hash, Some(payment_secret), events.pop().unwrap(), true, Some(payment_preimage));
-       claim_payment_along_route(&nodes[0], &[&[&nodes[1]]], false, payment_preimage);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1]]], payment_preimage)
+       );
 }
 
 #[test]
@@ -1592,7 +1589,9 @@ fn claimed_send_payment_idempotent() {
        // Claim the payment backwards, but note that the PaymentSent event is still pending and has
        // not been seen by the user. At this point, from the user perspective nothing has changed, so
        // we must remain just as idempotent as we were before.
-       do_claim_payment_along_route(&nodes[0], &[&[&nodes[1]]], false, first_payment_preimage);
+       do_claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1]]], first_payment_preimage)
+       );
 
        for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
                nodes[0].node.timer_tick_occurred();
@@ -1993,7 +1992,9 @@ fn do_test_intercepted_payment(test: InterceptTest) {
 
                let payment_preimage = nodes[2].node.get_payment_preimage(payment_hash, payment_secret).unwrap();
                expect_payment_claimable!(&nodes[2], payment_hash, payment_secret, amt_msat, Some(payment_preimage), nodes[2].node.get_our_node_id());
-               do_claim_payment_along_route(&nodes[0], &vec!(&vec!(&nodes[1], &nodes[2])[..]), false, payment_preimage);
+               do_claim_payment_along_route(
+                       ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage)
+               );
                let events = nodes[0].node.get_and_clear_pending_events();
                assert_eq!(events.len(), 2);
                match events[0] {
@@ -2142,9 +2143,11 @@ fn do_accept_underpaying_htlcs_config(num_mpp_parts: usize) {
                        assert_eq!(skimmed_fee_msat * num_mpp_parts as u64, counterparty_skimmed_fee_msat);
                        assert_eq!(nodes[2].node.get_our_node_id(), receiver_node_id.unwrap());
                        match purpose {
-                               crate::events::PaymentPurpose::InvoicePayment { payment_preimage: ev_payment_preimage,
-                                       payment_secret: ev_payment_secret, .. } =>
-                               {
+                               crate::events::PaymentPurpose::Bolt11InvoicePayment {
+                                       payment_preimage: ev_payment_preimage,
+                                       payment_secret: ev_payment_secret,
+                                       ..
+                               } => {
                                        assert_eq!(payment_preimage, ev_payment_preimage.unwrap());
                                        assert_eq!(payment_secret, *ev_payment_secret);
                                },
@@ -2281,7 +2284,9 @@ fn do_automatic_retries(test: AutoRetry) {
                let mut msg_events = nodes[0].node.get_and_clear_pending_msg_events();
                assert_eq!(msg_events.len(), 1);
                pass_along_path(&nodes[0], &[&nodes[1], &nodes[2]], amt_msat, payment_hash, Some(payment_secret), msg_events.pop().unwrap(), true, None);
-               claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], false, payment_preimage);
+               claim_payment_along_route(
+                       ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage)
+               );
        } else if test == AutoRetry::Spontaneous {
                nodes[0].node.send_spontaneous_payment_with_retry(Some(payment_preimage),
                        RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params,
@@ -2298,7 +2303,9 @@ fn do_automatic_retries(test: AutoRetry) {
                let mut msg_events = nodes[0].node.get_and_clear_pending_msg_events();
                assert_eq!(msg_events.len(), 1);
                pass_along_path(&nodes[0], &[&nodes[1], &nodes[2]], amt_msat, payment_hash, None, msg_events.pop().unwrap(), true, Some(payment_preimage));
-               claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], false, payment_preimage);
+               claim_payment_along_route(
+                       ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage)
+               );
        } else if test == AutoRetry::FailAttempts {
                // Ensure ChannelManager will not retry a payment if it has run out of payment attempts.
                nodes[0].node.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret),
@@ -3725,11 +3732,17 @@ fn do_test_custom_tlvs(spontaneous: bool, even_tlvs: bool, known_tlvs: bool) {
        match (known_tlvs, even_tlvs) {
                (true, _) => {
                        nodes[1].node.claim_funds_with_known_custom_tlvs(our_payment_preimage);
-                       let expected_total_fee_msat = pass_claimed_payment_along_route(ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1]]], our_payment_preimage));
+                       let expected_total_fee_msat = pass_claimed_payment_along_route(
+                               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1]]], our_payment_preimage)
+                                       .with_custom_tlvs(custom_tlvs)
+                       );
                        expect_payment_sent!(&nodes[0], our_payment_preimage, Some(expected_total_fee_msat));
                },
                (false, false) => {
-                       claim_payment(&nodes[0], &[&nodes[1]], our_payment_preimage);
+                       claim_payment_along_route(
+                               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1]]], our_payment_preimage)
+                                       .with_custom_tlvs(custom_tlvs)
+                       );
                },
                (false, true) => {
                        nodes[1].node.claim_funds(our_payment_preimage);
@@ -3813,15 +3826,15 @@ fn test_retry_custom_tlvs() {
        check_added_monitors!(nodes[0], 1);
        let mut events = nodes[0].node.get_and_clear_pending_msg_events();
        assert_eq!(events.len(), 1);
-       let payment_claimable = pass_along_path(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000,
-               payment_hash, Some(payment_secret), events.pop().unwrap(), true, None).unwrap();
-       match payment_claimable {
-               Event::PaymentClaimable { onion_fields, .. } => {
-                       assert_eq!(&onion_fields.unwrap().custom_tlvs()[..], &custom_tlvs[..]);
-               },
-               _ => panic!("Unexpected event"),
-       };
-       claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], false, payment_preimage);
+       let path = &[&nodes[1], &nodes[2]];
+       let args = PassAlongPathArgs::new(&nodes[0], path, 1_000_000, payment_hash, events.pop().unwrap())
+               .with_payment_secret(payment_secret)
+               .with_custom_tlvs(custom_tlvs.clone());
+       do_pass_along_path(args);
+       claim_payment_along_route(
+               ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[2]]], payment_preimage)
+                       .with_custom_tlvs(custom_tlvs)
+       );
 }
 
 #[test]
@@ -3952,8 +3965,10 @@ fn do_test_custom_tlvs_consistency(first_tlvs: Vec<(u64, Vec<u8>)>, second_tlvs:
                        _ => panic!("Unexpected event"),
                }
 
-               do_claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]],
-                       false, our_payment_preimage);
+               do_claim_payment_along_route(
+                       ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], our_payment_preimage)
+                               .with_custom_tlvs(expected_tlvs)
+               );
                expect_payment_sent(&nodes[0], our_payment_preimage, Some(Some(2000)), true, true);
        } else {
                // Expect fail back
@@ -4116,7 +4131,9 @@ fn do_test_payment_metadata_consistency(do_reload: bool, do_modify: bool) {
        } else {
                expect_pending_htlcs_forwardable!(nodes[3]);
                expect_payment_claimable!(nodes[3], payment_hash, payment_secret, amt_msat);
-               claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_preimage);
+               claim_payment_along_route(
+                       ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], payment_preimage)
+               );
        }
 }
 
@@ -4242,7 +4259,7 @@ fn peel_payment_onion_custom_tlvs() {
        let payment_hash = PaymentHash(Sha256::hash(&keysend_preimage.0).to_byte_array());
 
        let (onion_routing_packet, first_hop_msat, cltv_expiry) = onion_utils::create_payment_onion(
-               &secp_ctx, &route.paths[0], &session_priv, amt_msat, recipient_onion.clone(),
+               &secp_ctx, &route.paths[0], &session_priv, amt_msat, &recipient_onion,
                nodes[0].best_block_info().1, &payment_hash, &Some(keysend_preimage), prng_seed
        ).unwrap();
 
index 11cdd906abad191ec163c7b0a55766b3101fb223..f31a8e131e0469d49273713fd3c9d2b32e0319e3 100644 (file)
@@ -20,7 +20,7 @@ use bitcoin::secp256k1::{self, Secp256k1, SecretKey, PublicKey};
 
 use crate::sign::{NodeSigner, Recipient};
 use crate::events::{EventHandler, EventsProvider, MessageSendEvent, MessageSendEventsProvider};
-use crate::ln::ChannelId;
+use crate::ln::types::ChannelId;
 use crate::ln::features::{InitFeatures, NodeFeatures};
 use crate::ln::msgs;
 use crate::ln::msgs::{ChannelMessageHandler, LightningError, SocketAddress, OnionMessageHandler, RoutingMessageHandler};
@@ -28,7 +28,7 @@ use crate::util::ser::{VecWriter, Writeable, Writer};
 use crate::ln::peer_channel_encryptor::{PeerChannelEncryptor, NextNoiseStep, MessageBuf, MSG_BUF_ALLOC_SIZE};
 use crate::ln::wire;
 use crate::ln::wire::{Encode, Type};
-use crate::onion_message::messenger::{CustomOnionMessageHandler, PendingOnionMessage};
+use crate::onion_message::messenger::{CustomOnionMessageHandler, PendingOnionMessage, Responder, ResponseInstruction};
 use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
 use crate::onion_message::packet::OnionMessageContents;
 use crate::routing::gossip::{NodeId, NodeAlias};
@@ -36,9 +36,10 @@ use crate::util::atomic_counter::AtomicCounter;
 use crate::util::logger::{Level, Logger, WithContext};
 use crate::util::string::PrintableString;
 
+#[allow(unused_imports)]
 use crate::prelude::*;
+
 use crate::io;
-use alloc::collections::VecDeque;
 use crate::sync::{Mutex, MutexGuard, FairRwLock};
 use core::sync::atomic::{AtomicBool, AtomicU32, AtomicI32, Ordering};
 use core::{cmp, hash, fmt, mem};
@@ -116,10 +117,13 @@ impl RoutingMessageHandler for IgnoringMessageHandler {
        fn handle_query_short_channel_ids(&self, _their_node_id: &PublicKey, _msg: msgs::QueryShortChannelIds) -> Result<(), LightningError> { Ok(()) }
        fn provided_node_features(&self) -> NodeFeatures { NodeFeatures::empty() }
        fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures {
-               InitFeatures::empty()
+               let mut features = InitFeatures::empty();
+               features.set_gossip_queries_optional();
+               features
        }
        fn processing_queue_high(&self) -> bool { false }
 }
+
 impl OnionMessageHandler for IgnoringMessageHandler {
        fn handle_onion_message(&self, _their_node_id: &PublicKey, _msg: &msgs::OnionMessage) {}
        fn next_onion_message_for_peer(&self, _peer_node_id: PublicKey) -> Option<msgs::OnionMessage> { None }
@@ -131,12 +135,15 @@ impl OnionMessageHandler for IgnoringMessageHandler {
                InitFeatures::empty()
        }
 }
+
 impl OffersMessageHandler for IgnoringMessageHandler {
-       fn handle_message(&self, _msg: OffersMessage) -> Option<OffersMessage> { None }
+       fn handle_message(&self, _message: OffersMessage, _responder: Option<Responder>) -> ResponseInstruction<OffersMessage> {
+               ResponseInstruction::NoResponse
+       }
 }
 impl CustomOnionMessageHandler for IgnoringMessageHandler {
        type CustomMessage = Infallible;
-       fn handle_custom_message(&self, _msg: Infallible) -> Option<Infallible> {
+       fn handle_custom_message(&self, _message: Self::CustomMessage, _responder: Option<Responder>) -> ResponseInstruction<Self::CustomMessage> {
                // Since we always return `None` in the read the handle method should never be called.
                unreachable!();
        }
@@ -150,6 +157,7 @@ impl CustomOnionMessageHandler for IgnoringMessageHandler {
 
 impl OnionMessageContents for Infallible {
        fn tlv_type(&self) -> u64 { unreachable!(); }
+       fn msg_type(&self) -> &'static str { unreachable!(); }
 }
 
 impl Deref for IgnoringMessageHandler {
@@ -245,12 +253,15 @@ impl ChannelMessageHandler for ErroringMessageHandler {
        fn handle_stfu(&self, their_node_id: &PublicKey, msg: &msgs::Stfu) {
                ErroringMessageHandler::push_error(&self, their_node_id, msg.channel_id);
        }
+       #[cfg(splicing)]
        fn handle_splice(&self, their_node_id: &PublicKey, msg: &msgs::Splice) {
                ErroringMessageHandler::push_error(&self, their_node_id, msg.channel_id);
        }
+       #[cfg(splicing)]
        fn handle_splice_ack(&self, their_node_id: &PublicKey, msg: &msgs::SpliceAck) {
                ErroringMessageHandler::push_error(&self, their_node_id, msg.channel_id);
        }
+       #[cfg(splicing)]
        fn handle_splice_locked(&self, their_node_id: &PublicKey, msg: &msgs::SpliceLocked) {
                ErroringMessageHandler::push_error(&self, their_node_id, msg.channel_id);
        }
@@ -1323,7 +1334,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
 
        /// Append a message to a peer's pending outbound/write buffer
        fn enqueue_message<M: wire::Type>(&self, peer: &mut Peer, message: &M) {
-               let logger = WithContext::from(&self.logger, peer.their_node_id.map(|p| p.0), None);
+               let logger = WithContext::from(&self.logger, peer.their_node_id.map(|p| p.0), None, None);
                if is_gossip_msg(message.type_id()) {
                        log_gossip!(logger, "Enqueueing message {:?} to {}", message, log_pubkey!(peer.their_node_id.unwrap().0));
                } else {
@@ -1358,7 +1369,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                        macro_rules! try_potential_handleerror {
                                                ($peer: expr, $thing: expr) => {{
                                                        let res = $thing;
-                                                       let logger = WithContext::from(&self.logger, peer_node_id.map(|(id, _)| id), None);
+                                                       let logger = WithContext::from(&self.logger, peer_node_id.map(|(id, _)| id), None, None);
                                                        match res {
                                                                Ok(x) => x,
                                                                Err(e) => {
@@ -1430,7 +1441,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
 
                                                macro_rules! insert_node_id {
                                                        () => {
-                                                               let logger = WithContext::from(&self.logger, peer.their_node_id.map(|p| p.0), None);
+                                                               let logger = WithContext::from(&self.logger, peer.their_node_id.map(|p| p.0), None, None);
                                                                match self.node_id_to_descriptor.lock().unwrap().entry(peer.their_node_id.unwrap().0) {
                                                                        hash_map::Entry::Occupied(e) => {
                                                                                log_trace!(logger, "Got second connection with {}, closing", log_pubkey!(peer.their_node_id.unwrap().0));
@@ -1472,7 +1483,6 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                                                let networks = self.message_handler.chan_handler.get_chain_hashes();
                                                                let resp = msgs::Init { features, networks, remote_network_address: filter_addresses(peer.their_socket_address.clone()) };
                                                                self.enqueue_message(peer, &resp);
-                                                               peer.awaiting_pong_timer_tick_intervals = 0;
                                                        },
                                                        NextNoiseStep::ActThree => {
                                                                let their_node_id = try_potential_handleerror!(peer,
@@ -1485,7 +1495,6 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                                                let networks = self.message_handler.chan_handler.get_chain_hashes();
                                                                let resp = msgs::Init { features, networks, remote_network_address: filter_addresses(peer.their_socket_address.clone()) };
                                                                self.enqueue_message(peer, &resp);
-                                                               peer.awaiting_pong_timer_tick_intervals = 0;
                                                        },
                                                        NextNoiseStep::NoiseComplete => {
                                                                if peer.pending_read_is_header {
@@ -1510,7 +1519,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                                                        peer.pending_read_buffer.resize(18, 0);
                                                                        peer.pending_read_is_header = true;
 
-                                                                       let logger = WithContext::from(&self.logger, peer.their_node_id.map(|p| p.0), None);
+                                                                       let logger = WithContext::from(&self.logger, peer.their_node_id.map(|p| p.0), None, None);
                                                                        let message = match message_result {
                                                                                Ok(x) => x,
                                                                                Err(e) => {
@@ -1551,6 +1560,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                                                                                }
                                                                                                (msgs::DecodeError::BadLengthDescriptor, _) => return Err(PeerHandleError { }),
                                                                                                (msgs::DecodeError::Io(_), _) => return Err(PeerHandleError { }),
+                                                                                               (msgs::DecodeError::DangerousValue, _) => return Err(PeerHandleError { }),
                                                                                        }
                                                                                }
                                                                        };
@@ -1588,15 +1598,37 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
        }
 
        /// Process an incoming message and return a decision (ok, lightning error, peer handling error) regarding the next action with the peer
+       ///
        /// Returns the message back if it needs to be broadcasted to all other peers.
        fn handle_message(
                &self,
                peer_mutex: &Mutex<Peer>,
-               mut peer_lock: MutexGuard<Peer>,
-               message: wire::Message<<<CMH as core::ops::Deref>::Target as wire::CustomMessageReader>::CustomMessage>
-       ) -> Result<Option<wire::Message<<<CMH as core::ops::Deref>::Target as wire::CustomMessageReader>::CustomMessage>>, MessageHandlingError> {
+               peer_lock: MutexGuard<Peer>,
+               message: wire::Message<<<CMH as Deref>::Target as wire::CustomMessageReader>::CustomMessage>
+       ) -> Result<Option<wire::Message<<<CMH as Deref>::Target as wire::CustomMessageReader>::CustomMessage>>, MessageHandlingError> {
                let their_node_id = peer_lock.their_node_id.clone().expect("We know the peer's public key by the time we receive messages").0;
-               let logger = WithContext::from(&self.logger, Some(their_node_id), None);
+               let logger = WithContext::from(&self.logger, Some(their_node_id), None, None);
+
+               let message = match self.do_handle_message_holding_peer_lock(peer_lock, message, &their_node_id, &logger)? {
+                       Some(processed_message) => processed_message,
+                       None => return Ok(None),
+               };
+
+               self.do_handle_message_without_peer_lock(peer_mutex, message, &their_node_id, &logger)
+       }
+
+       // Conducts all message processing that requires us to hold the `peer_lock`.
+       //
+       // Returns `None` if the message was fully processed and otherwise returns the message back to
+       // allow it to be subsequently processed by `do_handle_message_without_peer_lock`.
+       fn do_handle_message_holding_peer_lock<'a>(
+               &self,
+               mut peer_lock: MutexGuard<Peer>,
+               message: wire::Message<<<CMH as Deref>::Target as wire::CustomMessageReader>::CustomMessage>,
+               their_node_id: &PublicKey,
+               logger: &WithContext<'a, L>
+       ) -> Result<Option<wire::Message<<<CMH as Deref>::Target as wire::CustomMessageReader>::CustomMessage>>, MessageHandlingError>
+       {
                peer_lock.received_message_since_timer_tick = true;
 
                // Need an Init as first message
@@ -1655,6 +1687,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                return Err(PeerHandleError { }.into());
                        }
 
+                       peer_lock.awaiting_pong_timer_tick_intervals = 0;
                        peer_lock.their_features = Some(msg.features);
                        return Ok(None);
                } else if peer_lock.their_features.is_none() {
@@ -1677,8 +1710,20 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                        peer_lock.received_channel_announce_since_backlogged = true;
                }
 
-               mem::drop(peer_lock);
+               Ok(Some(message))
+       }
 
+       // Conducts all message processing that doesn't require us to hold the `peer_lock`.
+       //
+       // Returns the message back if it needs to be broadcasted to all other peers.
+       fn do_handle_message_without_peer_lock<'a>(
+               &self,
+               peer_mutex: &Mutex<Peer>,
+               message: wire::Message<<<CMH as Deref>::Target as wire::CustomMessageReader>::CustomMessage>,
+               their_node_id: &PublicKey,
+               logger: &WithContext<'a, L>
+       ) -> Result<Option<wire::Message<<<CMH as Deref>::Target as wire::CustomMessageReader>::CustomMessage>>, MessageHandlingError>
+       {
                if is_gossip_msg(message.type_id()) {
                        log_gossip!(logger, "Received message {:?} from {}", message, log_pubkey!(their_node_id));
                } else {
@@ -1747,13 +1792,16 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                self.message_handler.chan_handler.handle_stfu(&their_node_id, &msg);
                        }
 
+                       #[cfg(splicing)]
                        // Splicing messages:
                        wire::Message::Splice(msg) => {
                                self.message_handler.chan_handler.handle_splice(&their_node_id, &msg);
                        }
+                       #[cfg(splicing)]
                        wire::Message::SpliceAck(msg) => {
                                self.message_handler.chan_handler.handle_splice_ack(&their_node_id, &msg);
                        }
+                       #[cfg(splicing)]
                        wire::Message::SpliceLocked(msg) => {
                                self.message_handler.chan_handler.handle_splice_locked(&their_node_id, &msg);
                        }
@@ -1880,7 +1928,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                Ok(should_forward)
        }
 
-       fn forward_broadcast_msg(&self, peers: &HashMap<Descriptor, Mutex<Peer>>, msg: &wire::Message<<<CMH as core::ops::Deref>::Target as wire::CustomMessageReader>::CustomMessage>, except_node: Option<&PublicKey>) {
+       fn forward_broadcast_msg(&self, peers: &HashMap<Descriptor, Mutex<Peer>>, msg: &wire::Message<<<CMH as Deref>::Target as wire::CustomMessageReader>::CustomMessage>, except_node: Option<&PublicKey>) {
                match msg {
                        wire::Message::ChannelAnnouncement(ref msg) => {
                                log_gossip!(self.logger, "Sending message to all peers except {:?} or the announced channel's counterparties: {:?}", except_node, msg);
@@ -1894,7 +1942,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                        }
                                        debug_assert!(peer.their_node_id.is_some());
                                        debug_assert!(peer.channel_encryptor.is_ready_for_encryption());
-                                       let logger = WithContext::from(&self.logger, peer.their_node_id.map(|p| p.0), None);
+                                       let logger = WithContext::from(&self.logger, peer.their_node_id.map(|p| p.0), None, None);
                                        if peer.buffer_full_drop_gossip_broadcast() {
                                                log_gossip!(logger, "Skipping broadcast message to {:?} as its outbound buffer is full", peer.their_node_id);
                                                continue;
@@ -1922,7 +1970,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                        }
                                        debug_assert!(peer.their_node_id.is_some());
                                        debug_assert!(peer.channel_encryptor.is_ready_for_encryption());
-                                       let logger = WithContext::from(&self.logger, peer.their_node_id.map(|p| p.0), None);
+                                       let logger = WithContext::from(&self.logger, peer.their_node_id.map(|p| p.0), None, None);
                                        if peer.buffer_full_drop_gossip_broadcast() {
                                                log_gossip!(logger, "Skipping broadcast message to {:?} as its outbound buffer is full", peer.their_node_id);
                                                continue;
@@ -1950,7 +1998,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                        }
                                        debug_assert!(peer.their_node_id.is_some());
                                        debug_assert!(peer.channel_encryptor.is_ready_for_encryption());
-                                       let logger = WithContext::from(&self.logger, peer.their_node_id.map(|p| p.0), None);
+                                       let logger = WithContext::from(&self.logger, peer.their_node_id.map(|p| p.0), None, None);
                                        if peer.buffer_full_drop_gossip_broadcast() {
                                                log_gossip!(logger, "Skipping broadcast message to {:?} as its outbound buffer is full", peer.their_node_id);
                                                continue;
@@ -2034,31 +2082,31 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                for event in events_generated.drain(..) {
                                        match event {
                                                MessageSendEvent::SendAcceptChannel { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.common_fields.temporary_channel_id)), "Handling SendAcceptChannel event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.common_fields.temporary_channel_id), None), "Handling SendAcceptChannel event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.common_fields.temporary_channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendAcceptChannelV2 { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.common_fields.temporary_channel_id)), "Handling SendAcceptChannelV2 event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.common_fields.temporary_channel_id), None), "Handling SendAcceptChannelV2 event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.common_fields.temporary_channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendOpenChannel { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.common_fields.temporary_channel_id)), "Handling SendOpenChannel event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.common_fields.temporary_channel_id), None), "Handling SendOpenChannel event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.common_fields.temporary_channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendOpenChannelV2 { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.common_fields.temporary_channel_id)), "Handling SendOpenChannelV2 event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.common_fields.temporary_channel_id), None), "Handling SendOpenChannelV2 event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.common_fields.temporary_channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendFundingCreated { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.temporary_channel_id)), "Handling SendFundingCreated event in peer_handler for node {} for channel {} (which becomes {})",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.temporary_channel_id), None), "Handling SendFundingCreated event in peer_handler for node {} for channel {} (which becomes {})",
                                                                        log_pubkey!(node_id),
                                                                        &msg.temporary_channel_id,
                                                                        ChannelId::v1_from_funding_txid(msg.funding_txid.as_byte_array(), msg.funding_output_index));
@@ -2067,107 +2115,107 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendFundingSigned { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendFundingSigned event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendFundingSigned event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendChannelReady { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendChannelReady event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendChannelReady event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendStfu { ref node_id, ref msg} => {
-                                                       let logger = WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id));
+                                                       let logger = WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None);
                                                        log_debug!(logger, "Handling SendStfu event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                }
                                                MessageSendEvent::SendSplice { ref node_id, ref msg} => {
-                                                       let logger = WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id));
+                                                       let logger = WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None);
                                                        log_debug!(logger, "Handling SendSplice event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                }
                                                MessageSendEvent::SendSpliceAck { ref node_id, ref msg} => {
-                                                       let logger = WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id));
+                                                       let logger = WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None);
                                                        log_debug!(logger, "Handling SendSpliceAck event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                }
                                                MessageSendEvent::SendSpliceLocked { ref node_id, ref msg} => {
-                                                       let logger = WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id));
+                                                       let logger = WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None);
                                                        log_debug!(logger, "Handling SendSpliceLocked event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                }
                                                MessageSendEvent::SendTxAddInput { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendTxAddInput event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendTxAddInput event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendTxAddOutput { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendTxAddOutput event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendTxAddOutput event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendTxRemoveInput { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendTxRemoveInput event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendTxRemoveInput event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendTxRemoveOutput { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendTxRemoveOutput event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendTxRemoveOutput event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendTxComplete { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendTxComplete event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendTxComplete event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendTxSignatures { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendTxSignatures event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendTxSignatures event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendTxInitRbf { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendTxInitRbf event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendTxInitRbf event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendTxAckRbf { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendTxAckRbf event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendTxAckRbf event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendTxAbort { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendTxAbort event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendTxAbort event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendAnnouncementSignatures { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendAnnouncementSignatures event in peer_handler for node {} for channel {})",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendAnnouncementSignatures event in peer_handler for node {} for channel {})",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref update_fail_malformed_htlcs, ref update_fee, ref commitment_signed } } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(commitment_signed.channel_id)), "Handling UpdateHTLCs event in peer_handler for node {} with {} adds, {} fulfills, {} fails for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(commitment_signed.channel_id), None), "Handling UpdateHTLCs event in peer_handler for node {} with {} adds, {} fulfills, {} fails for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        update_add_htlcs.len(),
                                                                        update_fulfill_htlcs.len(),
@@ -2192,31 +2240,31 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                                        self.enqueue_message(&mut *peer, commitment_signed);
                                                },
                                                MessageSendEvent::SendRevokeAndACK { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendRevokeAndACK event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendRevokeAndACK event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendClosingSigned { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendClosingSigned event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendClosingSigned event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendShutdown { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling Shutdown event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling Shutdown event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendChannelReestablish { ref node_id, ref msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id)), "Handling SendChannelReestablish event in peer_handler for node {} for channel {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id), None), "Handling SendChannelReestablish event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id),
                                                                        &msg.channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::SendChannelAnnouncement { ref node_id, ref msg, ref update_msg } => {
-                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), None), "Handling SendChannelAnnouncement event in peer_handler for node {} for short channel id {}",
+                                                       log_debug!(WithContext::from(&self.logger, Some(*node_id), None, None), "Handling SendChannelAnnouncement event in peer_handler for node {} for short channel id {}",
                                                                        log_pubkey!(node_id),
                                                                        msg.contents.short_channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
@@ -2254,12 +2302,12 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                                        }
                                                },
                                                MessageSendEvent::SendChannelUpdate { ref node_id, ref msg } => {
-                                                       log_trace!(WithContext::from(&self.logger, Some(*node_id), None), "Handling SendChannelUpdate event in peer_handler for node {} for channel {}",
+                                                       log_trace!(WithContext::from(&self.logger, Some(*node_id), None, None), "Handling SendChannelUpdate event in peer_handler for node {} for channel {}",
                                                                        log_pubkey!(node_id), msg.contents.short_channel_id);
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                },
                                                MessageSendEvent::HandleError { node_id, action } => {
-                                                       let logger = WithContext::from(&self.logger, Some(node_id), None);
+                                                       let logger = WithContext::from(&self.logger, Some(node_id), None, None);
                                                        match action {
                                                                msgs::ErrorAction::DisconnectPeer { msg } => {
                                                                        if let Some(msg) = msg.as_ref() {
@@ -2272,7 +2320,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                                                        // We do not have the peers write lock, so we just store that we're
                                                                        // about to disconnect the peer and do it after we finish
                                                                        // processing most messages.
-                                                                       let msg = msg.map(|msg| wire::Message::<<<CMH as core::ops::Deref>::Target as wire::CustomMessageReader>::CustomMessage>::Error(msg));
+                                                                       let msg = msg.map(|msg| wire::Message::<<<CMH as Deref>::Target as wire::CustomMessageReader>::CustomMessage>::Error(msg));
                                                                        peers_to_disconnect.insert(node_id, msg);
                                                                },
                                                                msgs::ErrorAction::DisconnectPeerWithWarning { msg } => {
@@ -2311,7 +2359,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                                                        self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
                                                }
                                                MessageSendEvent::SendReplyChannelRange { ref node_id, ref msg } => {
-                                                       log_gossip!(WithContext::from(&self.logger, Some(*node_id), None), "Handling SendReplyChannelRange event in peer_handler for node {} with num_scids={} first_blocknum={} number_of_blocks={}, sync_complete={}",
+                                                       log_gossip!(WithContext::from(&self.logger, Some(*node_id), None, None), "Handling SendReplyChannelRange event in peer_handler for node {} with num_scids={} first_blocknum={} number_of_blocks={}, sync_complete={}",
                                                                log_pubkey!(node_id),
                                                                msg.short_channel_ids.len(),
                                                                msg.first_blocknum,
@@ -2385,7 +2433,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
 
                debug_assert!(peer.their_node_id.is_some());
                if let Some((node_id, _)) = peer.their_node_id {
-                       log_trace!(WithContext::from(&self.logger, Some(node_id), None), "Disconnecting peer with id {} due to {}", node_id, reason);
+                       log_trace!(WithContext::from(&self.logger, Some(node_id), None, None), "Disconnecting peer with id {} due to {}", node_id, reason);
                        self.message_handler.chan_handler.peer_disconnected(&node_id);
                        self.message_handler.onion_message_handler.peer_disconnected(&node_id);
                }
@@ -2404,7 +2452,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
                        Some(peer_lock) => {
                                let peer = peer_lock.lock().unwrap();
                                if let Some((node_id, _)) = peer.their_node_id {
-                                       log_trace!(WithContext::from(&self.logger, Some(node_id), None), "Handling disconnection of peer {}", log_pubkey!(node_id));
+                                       log_trace!(WithContext::from(&self.logger, Some(node_id), None, None), "Handling disconnection of peer {}", log_pubkey!(node_id));
                                        let removed = self.node_id_to_descriptor.lock().unwrap().remove(&node_id);
                                        debug_assert!(removed.is_some(), "descriptor maps should be consistent");
                                        if !peer.handshake_complete() { return; }
@@ -2633,10 +2681,10 @@ mod tests {
        use crate::sign::{NodeSigner, Recipient};
        use crate::events;
        use crate::io;
-       use crate::ln::ChannelId;
+       use crate::ln::types::ChannelId;
        use crate::ln::features::{InitFeatures, NodeFeatures};
        use crate::ln::peer_channel_encryptor::PeerChannelEncryptor;
-       use crate::ln::peer_handler::{CustomMessageHandler, PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler, filter_addresses};
+       use crate::ln::peer_handler::{CustomMessageHandler, PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler, filter_addresses, ErroringMessageHandler, MAX_BUFFER_DRAIN_TICK_INTERVALS_PER_PEER};
        use crate::ln::{msgs, wire};
        use crate::ln::msgs::{LightningError, SocketAddress};
        use crate::util::test_utils;
@@ -2645,11 +2693,13 @@ mod tests {
        use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::secp256k1::{PublicKey, SecretKey};
 
-       use crate::prelude::*;
        use crate::sync::{Arc, Mutex};
        use core::convert::Infallible;
        use core::sync::atomic::{AtomicBool, Ordering};
 
+       #[allow(unused_imports)]
+       use crate::prelude::*;
+
        #[derive(Clone)]
        struct FileDescriptor {
                fd: u16,
@@ -3176,6 +3226,105 @@ mod tests {
                assert!(peers[0].read_event(&mut fd_a, &b_data).is_err());
        }
 
+       #[test]
+       fn test_inbound_conn_handshake_complete_awaiting_pong() {
+               // Test that we do not disconnect an outbound peer after the noise handshake completes due
+               // to a pong timeout for a ping that was never sent if a timer tick fires after we send act
+               // two of the noise handshake along with our init message but before we receive their init
+               // message.
+               let logger = test_utils::TestLogger::new();
+               let node_signer_a = test_utils::TestNodeSigner::new(SecretKey::from_slice(&[42; 32]).unwrap());
+               let node_signer_b = test_utils::TestNodeSigner::new(SecretKey::from_slice(&[43; 32]).unwrap());
+               let peer_a = PeerManager::new(MessageHandler {
+                       chan_handler: ErroringMessageHandler::new(),
+                       route_handler: IgnoringMessageHandler {},
+                       onion_message_handler: IgnoringMessageHandler {},
+                       custom_message_handler: IgnoringMessageHandler {},
+               }, 0, &[0; 32], &logger, &node_signer_a);
+               let peer_b = PeerManager::new(MessageHandler {
+                       chan_handler: ErroringMessageHandler::new(),
+                       route_handler: IgnoringMessageHandler {},
+                       onion_message_handler: IgnoringMessageHandler {},
+                       custom_message_handler: IgnoringMessageHandler {},
+               }, 0, &[1; 32], &logger, &node_signer_b);
+
+               let a_id = node_signer_a.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 mut fd_b = FileDescriptor {
+                       fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())),
+                       disconnect: Arc::new(AtomicBool::new(false)),
+               };
+
+               // Exchange messages with both peers until they both complete the init handshake.
+               let act_one = peer_b.new_outbound_connection(a_id, fd_b.clone(), None).unwrap();
+               peer_a.new_inbound_connection(fd_a.clone(), None).unwrap();
+
+               assert_eq!(peer_a.read_event(&mut fd_a, &act_one).unwrap(), false);
+               peer_a.process_events();
+
+               let act_two = fd_a.outbound_data.lock().unwrap().split_off(0);
+               assert_eq!(peer_b.read_event(&mut fd_b, &act_two).unwrap(), false);
+               peer_b.process_events();
+
+               // Calling this here triggers the race on inbound connections.
+               peer_b.timer_tick_occurred();
+
+               let act_three_with_init_b = fd_b.outbound_data.lock().unwrap().split_off(0);
+               assert!(!peer_a.peers.read().unwrap().get(&fd_a).unwrap().lock().unwrap().handshake_complete());
+               assert_eq!(peer_a.read_event(&mut fd_a, &act_three_with_init_b).unwrap(), false);
+               peer_a.process_events();
+               assert!(peer_a.peers.read().unwrap().get(&fd_a).unwrap().lock().unwrap().handshake_complete());
+
+               let init_a = fd_a.outbound_data.lock().unwrap().split_off(0);
+               assert!(!init_a.is_empty());
+
+               assert!(!peer_b.peers.read().unwrap().get(&fd_b).unwrap().lock().unwrap().handshake_complete());
+               assert_eq!(peer_b.read_event(&mut fd_b, &init_a).unwrap(), false);
+               peer_b.process_events();
+               assert!(peer_b.peers.read().unwrap().get(&fd_b).unwrap().lock().unwrap().handshake_complete());
+
+               // Make sure we're still connected.
+               assert_eq!(peer_b.peers.read().unwrap().len(), 1);
+
+               // B should send a ping on the first timer tick after `handshake_complete`.
+               assert!(fd_b.outbound_data.lock().unwrap().split_off(0).is_empty());
+               peer_b.timer_tick_occurred();
+               peer_b.process_events();
+               assert!(!fd_b.outbound_data.lock().unwrap().split_off(0).is_empty());
+
+               let mut send_warning = || {
+                       {
+                               let peers = peer_a.peers.read().unwrap();
+                               let mut peer_b = peers.get(&fd_a).unwrap().lock().unwrap();
+                               peer_a.enqueue_message(&mut peer_b, &msgs::WarningMessage {
+                                       channel_id: ChannelId([0; 32]),
+                                       data: "no disconnect plz".to_string(),
+                               });
+                       }
+                       peer_a.process_events();
+                       let msg = fd_a.outbound_data.lock().unwrap().split_off(0);
+                       assert!(!msg.is_empty());
+                       assert_eq!(peer_b.read_event(&mut fd_b, &msg).unwrap(), false);
+                       peer_b.process_events();
+               };
+
+               // Fire more ticks until we reach the pong timeout. We send any message except pong to
+               // pretend the connection is still alive.
+               send_warning();
+               for _ in 0..MAX_BUFFER_DRAIN_TICK_INTERVALS_PER_PEER {
+                       peer_b.timer_tick_occurred();
+                       send_warning();
+               }
+               assert_eq!(peer_b.peers.read().unwrap().len(), 1);
+
+               // One more tick should enforce the pong timeout.
+               peer_b.timer_tick_occurred();
+               assert_eq!(peer_b.peers.read().unwrap().len(), 0);
+       }
+
        #[test]
        fn test_filter_addresses(){
                // Tests the filter_addresses function.
index 3d799df8d7327d5f0895804f66c64e94cfa94b7d..badf17a42632db4731ebdcc041862ced2ec629a9 100644 (file)
@@ -18,7 +18,8 @@ use crate::ln::channelmanager::{MIN_CLTV_EXPIRY_DELTA, PaymentId, RecipientOnion
 use crate::routing::gossip::RoutingFees;
 use crate::routing::router::{PaymentParameters, RouteHint, RouteHintHop};
 use crate::ln::features::ChannelTypeFeatures;
-use crate::ln::{msgs, ChannelId};
+use crate::ln::msgs;
+use crate::ln::types::ChannelId;
 use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ChannelUpdate, ErrorAction};
 use crate::ln::wire::Encode;
 use crate::util::config::{UserConfig, MaxDustHTLCExposure};
@@ -26,12 +27,11 @@ use crate::util::ser::Writeable;
 use crate::util::test_utils;
 
 use crate::prelude::*;
-use core::default::Default;
 
 use crate::ln::functional_test_utils::*;
 
 use bitcoin::blockdata::constants::ChainHash;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 
 #[test]
 fn test_priv_forwarding_rejection() {
index 4422af1e8c70c63a81b93ece1aac38e2553d00a0..36b620c6bab7bb21317ee04cf5e42174463c5a1b 100644 (file)
@@ -16,7 +16,8 @@ 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::ln::{msgs, ChannelId};
+use crate::ln::msgs;
+use crate::ln::types::ChannelId;
 use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
 use crate::util::test_channel_signer::TestChannelSigner;
 use crate::util::test_utils;
@@ -27,7 +28,6 @@ use crate::util::config::UserConfig;
 use bitcoin::hash_types::BlockHash;
 
 use crate::prelude::*;
-use core::default::Default;
 use crate::sync::Mutex;
 
 use crate::ln::functional_test_utils::*;
@@ -412,7 +412,7 @@ fn test_manager_serialize_deserialize_inconsistent_monitor() {
        }
 
        let mut nodes_0_read = &nodes_0_serialized[..];
-       if let Err(msgs::DecodeError::InvalidValue) =
+       if let Err(msgs::DecodeError::DangerousValue) =
                <(BlockHash, ChannelManager<&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator, &test_utils::TestRouter, &test_utils::TestLogger>)>::read(&mut nodes_0_read, ChannelManagerReadArgs {
                default_config: UserConfig::default(),
                entropy_source: keys_manager,
index a31d9520dad392d94f4956e0f8546926cb917668..45008fcebfa8262dc4c8901dd48706d90323af02 100644 (file)
@@ -15,6 +15,8 @@ use crate::chain::transaction::OutPoint;
 use crate::chain::Confirm;
 use crate::events::{Event, MessageSendEventsProvider, ClosureReason, HTLCDestination, MessageSendEvent};
 use crate::ln::msgs::{ChannelMessageHandler, Init};
+use crate::ln::types::ChannelId;
+use crate::sign::OutputSpender;
 use crate::util::test_utils;
 use crate::util::ser::Writeable;
 use crate::util::string::UntrustedString;
@@ -25,7 +27,7 @@ use bitcoin::secp256k1::Secp256k1;
 
 use crate::prelude::*;
 
-use crate::ln::{functional_test_utils::*, ChannelId};
+use crate::ln::functional_test_utils::*;
 
 fn do_test_onchain_htlc_reorg(local_commitment: bool, claim: bool) {
        // Our on-chain HTLC-claim learning has a few properties worth testing:
@@ -198,7 +200,7 @@ fn test_counterparty_revoked_reorg() {
        let mut unrevoked_local_txn = get_local_commitment_txn!(nodes[0], chan.2);
        assert_eq!(unrevoked_local_txn.len(), 3); // commitment + 2 HTLC txn
        // Sort the unrevoked transactions in reverse order, ie commitment tx, then HTLC 1 then HTLC 3
-       unrevoked_local_txn.sort_unstable_by_key(|tx| 1_000_000 - tx.output.iter().map(|outp| outp.value).sum::<u64>());
+       unrevoked_local_txn.sort_unstable_by_key(|tx| 1_000_000 - tx.output.iter().map(|outp| outp.value.to_sat()).sum::<u64>());
 
        // Now mine A's old commitment transaction, which should close the channel, but take no action
        // on any of the HTLCs, at least until we get six confirmations (which we won't get).
@@ -465,7 +467,7 @@ fn test_set_outpoints_partial_claiming() {
        // Connect blocks on node B
        connect_blocks(&nodes[1], TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS + 1);
        check_closed_broadcast!(nodes[1], true);
-       check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed, [nodes[0].node.get_our_node_id()], 1000000);
+       check_closed_event!(nodes[1], 1, ClosureReason::HTLCsTimedOut, [nodes[0].node.get_our_node_id()], 1000000);
        check_added_monitors!(nodes[1], 1);
        // Verify node B broadcast 2 HTLC-timeout txn
        let partial_claim_tx = {
@@ -807,7 +809,7 @@ fn do_test_retries_own_commitment_broadcast_after_reorg(anchors: bool, revoked_c
        connect_blocks(&nodes[0], TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS + 1);
        check_closed_broadcast(&nodes[0], 1, true);
        check_added_monitors(&nodes[0], 1);
-       check_closed_event(&nodes[0], 1, ClosureReason::HolderForceClosed, false, &[nodes[1].node.get_our_node_id()], 100_000);
+       check_closed_event(&nodes[0], 1, ClosureReason::HTLCsTimedOut, false, &[nodes[1].node.get_our_node_id()], 100_000);
 
        {
                let mut txn = nodes[0].tx_broadcaster.txn_broadcast();
index dc733dd25a1f0ca154c773f1987d4861e4d30a23..ef75bb581b48b61ba4b63f4f5695989697981823 100644 (file)
@@ -1,20 +1,21 @@
 //! Abstractions for scripts used in the Lightning Network.
 
+use bitcoin::{WitnessProgram, WPubkeyHash, WScriptHash};
 use bitcoin::blockdata::opcodes::all::OP_PUSHBYTES_0 as SEGWIT_V0;
 use bitcoin::blockdata::script::{Script, ScriptBuf};
 use bitcoin::hashes::Hash;
-use bitcoin::hash_types::{WPubkeyHash, WScriptHash};
 use bitcoin::secp256k1::PublicKey;
-use bitcoin::address::WitnessProgram;
 
 use crate::ln::channelmanager;
 use crate::ln::features::InitFeatures;
 use crate::ln::msgs::DecodeError;
 use crate::util::ser::{Readable, Writeable, Writer};
 
-use core::convert::TryFrom;
 use crate::io;
 
+#[allow(unused_imports)]
+use crate::prelude::*;
+
 /// A script pubkey for shutting down a channel as defined by [BOLT #2].
 ///
 /// [BOLT #2]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md
@@ -65,12 +66,12 @@ impl ShutdownScript {
 
        /// Generates a P2WPKH script pubkey from the given [`WPubkeyHash`].
        pub fn new_p2wpkh(pubkey_hash: &WPubkeyHash) -> Self {
-               Self(ShutdownScriptImpl::Bolt2(ScriptBuf::new_v0_p2wpkh(pubkey_hash)))
+               Self(ShutdownScriptImpl::Bolt2(ScriptBuf::new_p2wpkh(pubkey_hash)))
        }
 
        /// Generates a P2WSH script pubkey from the given [`WScriptHash`].
        pub fn new_p2wsh(script_hash: &WScriptHash) -> Self {
-               Self(ShutdownScriptImpl::Bolt2(ScriptBuf::new_v0_p2wsh(script_hash)))
+               Self(ShutdownScriptImpl::Bolt2(ScriptBuf::new_p2wsh(script_hash)))
        }
 
        /// Generates a witness script pubkey from the given segwit version and program.
@@ -112,7 +113,7 @@ impl ShutdownScript {
 /// Check if a given script is compliant with BOLT 2's shutdown script requirements for the given
 /// counterparty features.
 pub(crate) fn is_bolt2_compliant(script: &Script, features: &InitFeatures) -> bool {
-       if script.is_p2pkh() || script.is_p2sh() || script.is_v0_p2wpkh() || script.is_v0_p2wsh() {
+       if script.is_p2pkh() || script.is_p2sh() || script.is_p2wpkh() || script.is_p2wsh() {
                true
        } else if features.supports_shutdown_anysegwit() {
                script.is_witness_program() && script.as_bytes()[0] != SEGWIT_V0.to_u8()
@@ -149,7 +150,7 @@ impl Into<ScriptBuf> for ShutdownScript {
        fn into(self) -> ScriptBuf {
                match self.0 {
                        ShutdownScriptImpl::Legacy(pubkey) =>
-                               ScriptBuf::new_v0_p2wpkh(&WPubkeyHash::hash(&pubkey.serialize())),
+                               ScriptBuf::new_p2wpkh(&WPubkeyHash::hash(&pubkey.serialize())),
                        ShutdownScriptImpl::Bolt2(script_pubkey) => script_pubkey,
                }
        }
@@ -167,13 +168,15 @@ impl core::fmt::Display for ShutdownScript{
 #[cfg(test)]
 mod shutdown_script_tests {
        use super::ShutdownScript;
+
+       use bitcoin::{WitnessProgram, WitnessVersion};
        use bitcoin::blockdata::opcodes;
        use bitcoin::blockdata::script::{Builder, ScriptBuf};
        use bitcoin::secp256k1::Secp256k1;
        use bitcoin::secp256k1::{PublicKey, SecretKey};
+
        use crate::ln::features::InitFeatures;
-       use core::convert::TryFrom;
-       use bitcoin::address::{WitnessProgram, WitnessVersion};
+       use crate::prelude::*;
 
        fn pubkey() -> bitcoin::key::PublicKey {
                let secp_ctx = Secp256k1::signing_only();
@@ -202,7 +205,7 @@ mod shutdown_script_tests {
        fn generates_p2wpkh_from_pubkey() {
                let pubkey = pubkey();
                let pubkey_hash = pubkey.wpubkey_hash().unwrap();
-               let p2wpkh_script = ScriptBuf::new_v0_p2wpkh(&pubkey_hash);
+               let p2wpkh_script = ScriptBuf::new_p2wpkh(&pubkey_hash);
 
                let shutdown_script = ShutdownScript::new_p2wpkh_from_pubkey(pubkey.inner);
                assert!(shutdown_script.is_compatible(&any_segwit_features()));
@@ -213,7 +216,7 @@ mod shutdown_script_tests {
        #[test]
        fn generates_p2wpkh_from_pubkey_hash() {
                let pubkey_hash = pubkey().wpubkey_hash().unwrap();
-               let p2wpkh_script = ScriptBuf::new_v0_p2wpkh(&pubkey_hash);
+               let p2wpkh_script = ScriptBuf::new_p2wpkh(&pubkey_hash);
 
                let shutdown_script = ShutdownScript::new_p2wpkh(&pubkey_hash);
                assert!(shutdown_script.is_compatible(&any_segwit_features()));
@@ -225,7 +228,7 @@ mod shutdown_script_tests {
        #[test]
        fn generates_p2wsh_from_script_hash() {
                let script_hash = redeem_script().wscript_hash();
-               let p2wsh_script = ScriptBuf::new_v0_p2wsh(&script_hash);
+               let p2wsh_script = ScriptBuf::new_p2wsh(&script_hash);
 
                let shutdown_script = ShutdownScript::new_p2wsh(&script_hash);
                assert!(shutdown_script.is_compatible(&any_segwit_features()));
index c6d0cc58b9bf299a1fc2d6e06a34a9a6bc0b3958..847ae784345910bc6730595495f553925b2c00a4 100644 (file)
@@ -16,7 +16,8 @@ use crate::chain::transaction::OutPoint;
 use crate::events::{Event, MessageSendEvent, HTLCDestination, MessageSendEventsProvider, ClosureReason};
 use crate::ln::channelmanager::{self, PaymentSendFailure, PaymentId, RecipientOnionFields, Retry, ChannelShutdownState, ChannelDetails};
 use crate::routing::router::{PaymentParameters, get_route, RouteParameters};
-use crate::ln::{ChannelId, msgs};
+use crate::ln::msgs;
+use crate::ln::types::ChannelId;
 use crate::ln::msgs::{ChannelMessageHandler, ErrorAction};
 use crate::ln::onion_utils::INVALID_ONION_BLINDING;
 use crate::ln::script::ShutdownScript;
@@ -25,18 +26,15 @@ use crate::util::test_utils::OnGetShutdownScriptpubkey;
 use crate::util::errors::APIError;
 use crate::util::config::UserConfig;
 use crate::util::string::UntrustedString;
+use crate::prelude::*;
 
-use bitcoin::{Transaction, TxOut};
+use bitcoin::{Transaction, TxOut, WitnessProgram, WitnessVersion};
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::locktime::absolute::LockTime;
 use bitcoin::blockdata::script::Builder;
 use bitcoin::blockdata::opcodes;
-use bitcoin::network::constants::Network;
-use bitcoin::address::{WitnessProgram, WitnessVersion};
-
-use regex;
-
-use core::default::Default;
-use std::convert::TryFrom;
+use bitcoin::network::Network;
+use bitcoin::transaction::Version;
 
 use crate::ln::functional_test_utils::*;
 
@@ -1172,17 +1170,17 @@ fn do_test_closing_signed_reinit_timeout(timeout_step: TimeoutStep) {
        assert_eq!(txn[0].output.len(), 2);
 
        if timeout_step != TimeoutStep::NoTimeout {
-               assert!((txn[0].output[0].script_pubkey.is_v0_p2wpkh() &&
-                        txn[0].output[1].script_pubkey.is_v0_p2wsh()) ||
-                       (txn[0].output[1].script_pubkey.is_v0_p2wpkh() &&
-                        txn[0].output[0].script_pubkey.is_v0_p2wsh()));
+               assert!((txn[0].output[0].script_pubkey.is_p2wpkh() &&
+                        txn[0].output[1].script_pubkey.is_p2wsh()) ||
+                       (txn[0].output[1].script_pubkey.is_p2wpkh() &&
+                        txn[0].output[0].script_pubkey.is_p2wsh()));
                check_closed_broadcast!(nodes[1], true);
                check_added_monitors!(nodes[1], 1);
                check_closed_event!(nodes[1], 1, ClosureReason::ProcessingError { err: "closing_signed negotiation failed to finish within two timer ticks".to_string() }
                        , [nodes[0].node.get_our_node_id()], 100000);
        } else {
-               assert!(txn[0].output[0].script_pubkey.is_v0_p2wpkh());
-               assert!(txn[0].output[1].script_pubkey.is_v0_p2wpkh());
+               assert!(txn[0].output[0].script_pubkey.is_p2wpkh());
+               assert!(txn[0].output[1].script_pubkey.is_p2wpkh());
 
                let events = nodes[1].node.get_and_clear_pending_msg_events();
                assert_eq!(events.len(), 1);
@@ -1405,32 +1403,62 @@ fn batch_funding_failure() {
        let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
        let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
 
-       exchange_open_accept_chan(&nodes[0], &nodes[1], 1_000_000, 0);
-       exchange_open_accept_chan(&nodes[0], &nodes[2], 1_000_000, 0);
+       let temp_chan_id_a = exchange_open_accept_chan(&nodes[0], &nodes[1], 1_000_000, 0);
+       let temp_chan_id_b = exchange_open_accept_chan(&nodes[0], &nodes[2], 1_000_000, 0);
 
        let events = nodes[0].node.get_and_clear_pending_events();
        assert_eq!(events.len(), 2);
        // Build a transaction which only has the output for one of the two channels we're trying to
        // confirm. Previously this led to a deadlock in channel closure handling.
-       let mut tx = Transaction { version: 2, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
+       let mut tx = Transaction { version: Version::TWO, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
        let mut chans = Vec::new();
        for (idx, ev) in events.iter().enumerate() {
                if let Event::FundingGenerationReady { temporary_channel_id, counterparty_node_id, output_script, .. } = ev {
                        if idx == 0 {
-                               tx.output.push(TxOut { value: 1_000_000, script_pubkey: output_script.clone() });
+                               tx.output.push(TxOut { value: Amount::from_sat(1_000_000), script_pubkey: output_script.clone() });
                        }
                        chans.push((temporary_channel_id, counterparty_node_id));
                } else { panic!(); }
        }
 
-       // We should probably end up with an error for both channels, but currently we don't generate
-       // an error for the failing channel itself.
        let err = "Error in transaction funding: Misuse error: No output matched the script_pubkey and value in the FundingGenerationReady event".to_string();
-       let close = [ExpectedCloseEvent::from_id_reason(ChannelId::v1_from_funding_txid(tx.txid().as_ref(), 0), true, ClosureReason::ProcessingError { err })];
+       let temp_err = "No output matched the script_pubkey and value in the FundingGenerationReady event".to_string();
+       let post_funding_chan_id_a = ChannelId::v1_from_funding_txid(tx.txid().as_ref(), 0);
+       let close = [
+               ExpectedCloseEvent::from_id_reason(post_funding_chan_id_a, true, ClosureReason::ProcessingError { err: err.clone() }),
+               ExpectedCloseEvent::from_id_reason(temp_chan_id_b, false, ClosureReason::ProcessingError { err: temp_err }),
+       ];
 
        nodes[0].node.batch_funding_transaction_generated(&chans, tx).unwrap_err();
 
-       get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
+       let msgs = nodes[0].node.get_and_clear_pending_msg_events();
+       assert_eq!(msgs.len(), 3);
+       // We currently spuriously send `FundingCreated` for the first channel and then immediately
+       // fail both channels, which isn't ideal but should be fine.
+       assert!(msgs.iter().any(|msg| {
+               if let MessageSendEvent::HandleError { action: msgs::ErrorAction::SendErrorMessage {
+                       msg: msgs::ErrorMessage { channel_id, .. }, ..
+               }, .. } = msg {
+                       *channel_id == temp_chan_id_b
+               } else { false }
+       }));
+       let funding_created_pos = msgs.iter().position(|msg| {
+               if let MessageSendEvent::SendFundingCreated { msg: msgs::FundingCreated { temporary_channel_id, .. }, .. } = msg {
+                       assert_eq!(*temporary_channel_id, temp_chan_id_a);
+                       true
+               } else { false }
+       }).unwrap();
+       let funded_channel_close_pos = msgs.iter().position(|msg| {
+               if let MessageSendEvent::HandleError { action: msgs::ErrorAction::SendErrorMessage {
+                       msg: msgs::ErrorMessage { channel_id, .. }, ..
+               }, .. } = msg {
+                       *channel_id == post_funding_chan_id_a
+               } else { false }
+       }).unwrap();
+
+       // The error message uses the funded channel_id so must come after the funding_created
+       assert!(funded_channel_close_pos > funding_created_pos);
+
        check_closed_events(&nodes[0], &close);
        assert_eq!(nodes[0].node.list_channels().len(), 0);
 }
diff --git a/lightning/src/ln/types.rs b/lightning/src/ln/types.rs
new file mode 100644 (file)
index 0000000..c6fa598
--- /dev/null
@@ -0,0 +1,277 @@
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Various wrapper types (most around 32-byte arrays) for use in lightning.
+
+use crate::chain::transaction::OutPoint;
+use crate::io;
+use crate::ln::msgs::DecodeError;
+use crate::sign::EntropySource;
+use crate::util::ser::{Readable, Writeable, Writer};
+use super::channel_keys::RevocationBasepoint;
+
+#[allow(unused_imports)]
+use crate::prelude::*;
+
+use bitcoin::hashes::{
+       Hash as _,
+       HashEngine as _,
+       sha256::Hash as Sha256,
+};
+use core::fmt;
+use core::ops::Deref;
+
+/// A unique 32-byte identifier for a channel.
+/// Depending on how the ID is generated, several varieties are distinguished
+/// (but all are stored as 32 bytes):
+///   _v1_ and _temporary_.
+/// A _v1_ channel ID is generated based on funding tx outpoint (txid & index).
+/// A _temporary_ ID is generated randomly.
+/// (Later revocation-point-based _v2_ is a possibility.)
+/// The variety (context) is not stored, it is relevant only at creation.
+#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+pub struct ChannelId(pub [u8; 32]);
+
+impl ChannelId {
+       /// Create _v1_ channel ID based on a funding TX ID and output index
+       pub fn v1_from_funding_txid(txid: &[u8; 32], output_index: u16) -> Self {
+               let mut res = [0; 32];
+               res[..].copy_from_slice(&txid[..]);
+               res[30] ^= ((output_index >> 8) & 0xff) as u8;
+               res[31] ^= ((output_index >> 0) & 0xff) as u8;
+               Self(res)
+       }
+
+       /// Create _v1_ channel ID from a funding tx outpoint
+       pub fn v1_from_funding_outpoint(outpoint: OutPoint) -> Self {
+               Self::v1_from_funding_txid(outpoint.txid.as_byte_array(), outpoint.index)
+       }
+
+       /// Create a _temporary_ channel ID randomly, based on an entropy source.
+       pub fn temporary_from_entropy_source<ES: Deref>(entropy_source: &ES) -> Self
+       where ES::Target: EntropySource {
+               Self(entropy_source.get_secure_random_bytes())
+       }
+
+       /// Generic constructor; create a new channel ID from the provided data.
+       /// Use a more specific `*_from_*` constructor when possible.
+       pub fn from_bytes(data: [u8; 32]) -> Self {
+               Self(data)
+       }
+
+       /// Create a channel ID consisting of all-zeros data (e.g. when uninitialized or a placeholder).
+       pub fn new_zero() -> Self {
+               Self([0; 32])
+       }
+
+       /// Check whether ID is consisting of all zeros (uninitialized)
+       pub fn is_zero(&self) -> bool {
+               self.0[..] == [0; 32]
+       }
+
+       /// Create _v2_ channel ID by concatenating the holder revocation basepoint with the counterparty
+       /// revocation basepoint and hashing the result. The basepoints will be concatenated in increasing
+       /// sorted order.
+       pub fn v2_from_revocation_basepoints(
+               ours: &RevocationBasepoint,
+               theirs: &RevocationBasepoint,
+       ) -> Self {
+               let ours = ours.0.serialize();
+               let theirs = theirs.0.serialize();
+               let (lesser, greater) = if ours < theirs {
+                       (ours, theirs)
+               } else {
+                       (theirs, ours)
+               };
+               let mut engine = Sha256::engine();
+               engine.input(&lesser[..]);
+               engine.input(&greater[..]);
+               Self(Sha256::from_engine(engine).to_byte_array())
+       }
+
+       /// Create temporary _v2_ channel ID by concatenating a zeroed out basepoint with the holder
+       /// revocation basepoint and hashing the result.
+       pub fn temporary_v2_from_revocation_basepoint(our_revocation_basepoint: &RevocationBasepoint) -> Self {
+               Self(Sha256::hash(&[[0u8; 33], our_revocation_basepoint.0.serialize()].concat()).to_byte_array())
+       }
+}
+
+impl Writeable for ChannelId {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               self.0.write(w)
+       }
+}
+
+impl Readable for ChannelId {
+       fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
+               let buf: [u8; 32] = Readable::read(r)?;
+               Ok(ChannelId(buf))
+       }
+}
+
+impl fmt::Display for ChannelId {
+       fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+               crate::util::logger::DebugBytes(&self.0).fmt(f)
+       }
+}
+
+
+/// The payment hash is the hash of the [`PaymentPreimage`] which is the value used to lock funds
+/// in HTLCs while they transit the lightning network.
+///
+/// This is not exported to bindings users as we just use [u8; 32] directly
+#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
+pub struct PaymentHash(pub [u8; 32]);
+
+impl core::fmt::Display for PaymentHash {
+       fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+               crate::util::logger::DebugBytes(&self.0).fmt(f)
+       }
+}
+
+/// The payment preimage is the "secret key" which is used to claim the funds of an HTLC on-chain
+/// or in a lightning channel.
+///
+/// This is not exported to bindings users as we just use [u8; 32] directly
+#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
+pub struct PaymentPreimage(pub [u8; 32]);
+
+impl core::fmt::Display for PaymentPreimage {
+       fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+               crate::util::logger::DebugBytes(&self.0).fmt(f)
+       }
+}
+
+/// Converts a `PaymentPreimage` into a `PaymentHash` by hashing the preimage with SHA256.
+impl From<PaymentPreimage> for PaymentHash {
+       fn from(value: PaymentPreimage) -> Self {
+               PaymentHash(Sha256::hash(&value.0).to_byte_array())
+       }
+}
+
+/// The payment secret is used to authenticate the sender of an HTLC to the recipient and tie
+/// multi-part HTLCs together into a single payment.
+///
+/// This is not exported to bindings users as we just use [u8; 32] directly
+#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd)]
+pub struct PaymentSecret(pub [u8; 32]);
+
+use bech32::{Base32Len, FromBase32, ToBase32, WriteBase32, u5};
+
+impl FromBase32 for PaymentSecret {
+       type Err = bech32::Error;
+
+       fn from_base32(field_data: &[u5]) -> Result<PaymentSecret, bech32::Error> {
+               if field_data.len() != 52 {
+                       return Err(bech32::Error::InvalidLength)
+               } else {
+                       let data_bytes = Vec::<u8>::from_base32(field_data)?;
+                       let mut payment_secret = [0; 32];
+                       payment_secret.copy_from_slice(&data_bytes);
+                       Ok(PaymentSecret(payment_secret))
+               }
+       }
+}
+
+impl ToBase32 for PaymentSecret {
+       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
+               (&self.0[..]).write_base32(writer)
+       }
+}
+
+impl Base32Len for PaymentSecret {
+       fn base32_len(&self) -> usize {
+               52
+       }
+}
+
+#[cfg(test)]
+mod tests {
+       use bitcoin::hashes::{
+               Hash as _,
+               HashEngine as _,
+               hex::FromHex as _,
+               sha256::Hash as Sha256,
+       };
+       use bitcoin::secp256k1::PublicKey;
+       use hex::DisplayHex;
+
+       use super::ChannelId;
+       use crate::ln::channel_keys::RevocationBasepoint;
+       use crate::util::ser::{Readable, Writeable};
+       use crate::util::test_utils;
+       use crate::prelude::*;
+       use crate::io;
+
+       #[test]
+       fn test_channel_id_v1_from_funding_txid() {
+               let channel_id = ChannelId::v1_from_funding_txid(&[2; 32], 1);
+               assert_eq!(channel_id.0.as_hex().to_string(), "0202020202020202020202020202020202020202020202020202020202020203");
+       }
+
+       #[test]
+       fn test_channel_id_new_from_data() {
+               let data: [u8; 32] = [2; 32];
+               let channel_id = ChannelId::from_bytes(data.clone());
+               assert_eq!(channel_id.0, data);
+       }
+
+       #[test]
+       fn test_channel_id_equals() {
+               let channel_id11 = ChannelId::v1_from_funding_txid(&[2; 32], 2);
+               let channel_id12 = ChannelId::v1_from_funding_txid(&[2; 32], 2);
+               let channel_id21 = ChannelId::v1_from_funding_txid(&[2; 32], 42);
+               assert_eq!(channel_id11, channel_id12);
+               assert_ne!(channel_id11, channel_id21);
+       }
+
+       #[test]
+       fn test_channel_id_write_read() {
+               let data: [u8; 32] = [2; 32];
+               let channel_id = ChannelId::from_bytes(data.clone());
+
+               let mut w = test_utils::TestVecWriter(Vec::new());
+               channel_id.write(&mut w).unwrap();
+
+               let channel_id_2 = ChannelId::read(&mut io::Cursor::new(&w.0)).unwrap();
+               assert_eq!(channel_id_2, channel_id);
+               assert_eq!(channel_id_2.0, data);
+       }
+
+       #[test]
+       fn test_channel_id_display() {
+               let channel_id = ChannelId::v1_from_funding_txid(&[2; 32], 1);
+               assert_eq!(format!("{}", &channel_id), "0202020202020202020202020202020202020202020202020202020202020203");
+       }
+
+       #[test]
+       fn test_channel_id_v2_from_basepoints() {
+               // Ours greater than theirs
+               let ours = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap());
+               let theirs = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap());
+
+               let mut engine = Sha256::engine();
+               engine.input(&theirs.0.serialize());
+               engine.input(&ours.0.serialize());
+               let expected_id = ChannelId(Sha256::from_engine(engine).to_byte_array());
+
+               assert_eq!(ChannelId::v2_from_revocation_basepoints(&ours, &theirs), expected_id);
+
+               // Theirs greater than ours
+               let ours = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap());
+               let theirs = RevocationBasepoint(PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap());
+
+               let mut engine = Sha256::engine();
+               engine.input(&ours.0.serialize());
+               engine.input(&theirs.0.serialize());
+               let expected_id = ChannelId(Sha256::from_engine(engine).to_byte_array());
+
+               assert_eq!(ChannelId::v2_from_revocation_basepoints(&ours, &theirs), expected_id);
+       }
+}
index 5087df33b83fee55bfd7edc788ae13459deb486e..55e31399ae10074d2e72f1b04a4c1c71ed1808df 100644 (file)
@@ -60,8 +60,11 @@ pub(crate) enum Message<T> where T: core::fmt::Debug + Type + TestEq {
        FundingCreated(msgs::FundingCreated),
        FundingSigned(msgs::FundingSigned),
        Stfu(msgs::Stfu),
+       #[cfg(splicing)]
        Splice(msgs::Splice),
+       #[cfg(splicing)]
        SpliceAck(msgs::SpliceAck),
+       #[cfg(splicing)]
        SpliceLocked(msgs::SpliceLocked),
        TxAddInput(msgs::TxAddInput),
        TxAddOutput(msgs::TxAddOutput),
@@ -115,8 +118,11 @@ impl<T> Writeable for Message<T> where T: core::fmt::Debug + Type + TestEq {
                        &Message::FundingCreated(ref msg) => msg.write(writer),
                        &Message::FundingSigned(ref msg) => msg.write(writer),
                        &Message::Stfu(ref msg) => msg.write(writer),
+                       #[cfg(splicing)]
                        &Message::Splice(ref msg) => msg.write(writer),
+                       #[cfg(splicing)]
                        &Message::SpliceAck(ref msg) => msg.write(writer),
+                       #[cfg(splicing)]
                        &Message::SpliceLocked(ref msg) => msg.write(writer),
                        &Message::TxAddInput(ref msg) => msg.write(writer),
                        &Message::TxAddOutput(ref msg) => msg.write(writer),
@@ -170,8 +176,11 @@ impl<T> Type for Message<T> where T: core::fmt::Debug + Type + TestEq {
                        &Message::FundingCreated(ref msg) => msg.type_id(),
                        &Message::FundingSigned(ref msg) => msg.type_id(),
                        &Message::Stfu(ref msg) => msg.type_id(),
+                       #[cfg(splicing)]
                        &Message::Splice(ref msg) => msg.type_id(),
+                       #[cfg(splicing)]
                        &Message::SpliceAck(ref msg) => msg.type_id(),
+                       #[cfg(splicing)]
                        &Message::SpliceLocked(ref msg) => msg.type_id(),
                        &Message::TxAddInput(ref msg) => msg.type_id(),
                        &Message::TxAddOutput(ref msg) => msg.type_id(),
@@ -270,15 +279,18 @@ fn do_read<R: io::Read, T, H: core::ops::Deref>(buffer: &mut R, message_type: u1
                msgs::FundingSigned::TYPE => {
                        Ok(Message::FundingSigned(Readable::read(buffer)?))
                },
+               #[cfg(splicing)]
                msgs::Splice::TYPE => {
                        Ok(Message::Splice(Readable::read(buffer)?))
                },
                msgs::Stfu::TYPE => {
                        Ok(Message::Stfu(Readable::read(buffer)?))
                },
+               #[cfg(splicing)]
                msgs::SpliceAck::TYPE => {
                        Ok(Message::SpliceAck(Readable::read(buffer)?))
                },
+               #[cfg(splicing)]
                msgs::SpliceLocked::TYPE => {
                        Ok(Message::SpliceLocked(Readable::read(buffer)?))
                },
@@ -617,7 +629,6 @@ impl Encode for msgs::GossipTimestampFilter {
 mod tests {
        use super::*;
        use crate::prelude::*;
-       use core::convert::TryInto;
        use crate::ln::peer_handler::IgnoringMessageHandler;
 
        // Big-endian wire encoding of Pong message (type = 19, byteslen = 2).
index fc8371023484d8ade7a2708340a0e2402685d710..5482e6f83ea48e4139beb6b1d367aba55e5851eb 100644 (file)
@@ -1,4 +1,4 @@
-// This file is Copyright its original authors, visible in version control
+    // 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
 //! extern crate lightning;
 //!
 //! use bitcoin::hashes::Hash;
-//! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
+//! use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, SecretKey};
 //! use core::convert::TryFrom;
 //! use lightning::offers::invoice::UnsignedBolt12Invoice;
 //! use lightning::offers::invoice_request::InvoiceRequest;
 //! use lightning::offers::refund::Refund;
 //! use lightning::util::ser::Writeable;
 //!
-//! # use lightning::ln::PaymentHash;
+//! # use lightning::ln::types::PaymentHash;
 //! # use lightning::offers::invoice::{BlindedPayInfo, ExplicitSigningPubkey, InvoiceBuilder};
 //! # use lightning::blinded_path::BlindedPath;
 //! #
@@ -39,7 +39,7 @@
 //! let payment_paths = create_payment_paths();
 //! let payment_hash = create_payment_hash();
 //! let secp_ctx = Secp256k1::new();
-//! let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32])?);
+//! let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32])?);
 //! let pubkey = PublicKey::from(keys);
 //! let wpubkey_hash = bitcoin::key::PublicKey::new(pubkey).wpubkey_hash().unwrap();
 //! let mut buffer = Vec::new();
@@ -71,7 +71,7 @@
 //! # let payment_paths = create_payment_paths();
 //! # let payment_hash = create_payment_hash();
 //! # let secp_ctx = Secp256k1::new();
-//! # let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32])?);
+//! # let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32])?);
 //! # let pubkey = PublicKey::from(keys);
 //! # let wpubkey_hash = bitcoin::key::PublicKey::new(pubkey).wpubkey_hash().unwrap();
 //! # let mut buffer = Vec::new();
 //!
 //! ```
 
+use bitcoin::{WitnessProgram, Network, WitnessVersion, WPubkeyHash, WScriptHash};
 use bitcoin::blockdata::constants::ChainHash;
-use bitcoin::hash_types::{WPubkeyHash, WScriptHash};
-use bitcoin::hashes::Hash;
-use bitcoin::network::constants::Network;
-use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, self};
+use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, self};
 use bitcoin::secp256k1::schnorr::Signature;
-use bitcoin::address::{Address, Payload, WitnessProgram, WitnessVersion};
+use bitcoin::address::{Address, Payload};
 use bitcoin::key::TweakedPublicKey;
-use core::convert::{AsRef, TryFrom};
 use core::time::Duration;
+use core::hash::{Hash, Hasher};
 use crate::io;
 use crate::blinded_path::BlindedPath;
-use crate::ln::PaymentHash;
+use crate::ln::types::PaymentHash;
 use crate::ln::channelmanager::PaymentId;
 use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures, InvoiceRequestFeatures, OfferFeatures};
 use crate::ln::inbound_payment::ExpandedKey;
@@ -129,6 +127,7 @@ use crate::offers::signer;
 use crate::util::ser::{HighZeroBytesDroppedBigSize, Iterable, SeekReadable, WithoutLength, Writeable, Writer};
 use crate::util::string::PrintableString;
 
+#[allow(unused_imports)]
 use crate::prelude::*;
 
 #[cfg(feature = "std")]
@@ -201,7 +200,7 @@ pub struct ExplicitSigningPubkey {}
 /// [`Bolt12Invoice::signing_pubkey`] was derived.
 ///
 /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-pub struct DerivedSigningPubkey(KeyPair);
+pub struct DerivedSigningPubkey(Keypair);
 
 impl SigningPubkeyStrategy for ExplicitSigningPubkey {}
 impl SigningPubkeyStrategy for DerivedSigningPubkey {}
@@ -210,10 +209,9 @@ macro_rules! invoice_explicit_signing_pubkey_builder_methods { ($self: ident, $s
        #[cfg_attr(c_bindings, allow(dead_code))]
        pub(super) fn for_offer(
                invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
-               created_at: Duration, payment_hash: PaymentHash
+               created_at: Duration, payment_hash: PaymentHash, signing_pubkey: PublicKey
        ) -> Result<Self, Bolt12SemanticError> {
                let amount_msats = Self::amount_msats(invoice_request)?;
-               let signing_pubkey = invoice_request.contents.inner.offer.signing_pubkey();
                let contents = InvoiceContents::ForOffer {
                        invoice_request: invoice_request.contents.clone(),
                        fields: Self::fields(
@@ -269,10 +267,10 @@ macro_rules! invoice_derived_signing_pubkey_builder_methods { ($self: ident, $se
        #[cfg_attr(c_bindings, allow(dead_code))]
        pub(super) fn for_offer_using_keys(
                invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
-               created_at: Duration, payment_hash: PaymentHash, keys: KeyPair
+               created_at: Duration, payment_hash: PaymentHash, keys: Keypair
        ) -> Result<Self, Bolt12SemanticError> {
                let amount_msats = Self::amount_msats(invoice_request)?;
-               let signing_pubkey = invoice_request.contents.inner.offer.signing_pubkey();
+               let signing_pubkey = keys.public_key();
                let contents = InvoiceContents::ForOffer {
                        invoice_request: invoice_request.contents.clone(),
                        fields: Self::fields(
@@ -286,7 +284,7 @@ macro_rules! invoice_derived_signing_pubkey_builder_methods { ($self: ident, $se
        #[cfg_attr(c_bindings, allow(dead_code))]
        pub(super) fn for_refund_using_keys(
                refund: &'a Refund, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, created_at: Duration,
-               payment_hash: PaymentHash, keys: KeyPair,
+               payment_hash: PaymentHash, keys: Keypair,
        ) -> Result<Self, Bolt12SemanticError> {
                let amount_msats = refund.amount_msats();
                let signing_pubkey = keys.public_key();
@@ -390,6 +388,7 @@ macro_rules! invoice_builder_methods { (
        /// Successive calls to this method will add another address. Caller is responsible for not
        /// adding duplicate addresses and only calling if capable of receiving to P2WSH addresses.
        pub fn fallback_v0_p2wsh($($self_mut)* $self: $self_type, script_hash: &WScriptHash) -> $return_type {
+               use bitcoin::hashes::Hash;
                let address = FallbackAddress {
                        version: WitnessVersion::V0.to_num(),
                        program: Vec::from(script_hash.to_byte_array()),
@@ -403,6 +402,7 @@ macro_rules! invoice_builder_methods { (
        /// Successive calls to this method will add another address. Caller is responsible for not
        /// adding duplicate addresses and only calling if capable of receiving to P2WPKH addresses.
        pub fn fallback_v0_p2wpkh($($self_mut)* $self: $self_type, pubkey_hash: &WPubkeyHash) -> $return_type {
+               use bitcoin::hashes::Hash;
                let address = FallbackAddress {
                        version: WitnessVersion::V0.to_num(),
                        program: Vec::from(pubkey_hash.to_byte_array()),
@@ -502,6 +502,7 @@ for InvoiceBuilder<'a, DerivedSigningPubkey> {
 ///
 /// This is serialized as a TLV stream, which includes TLV records from the originating message. As
 /// such, it may include unknown, odd TLV records.
+#[derive(Clone)]
 pub struct UnsignedBolt12Invoice {
        bytes: Vec<u8>,
        contents: InvoiceContents,
@@ -544,7 +545,7 @@ impl UnsignedBolt12Invoice {
                let mut bytes = Vec::new();
                unsigned_tlv_stream.write(&mut bytes).unwrap();
 
-               let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
+               let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
 
                Self { bytes, contents, tagged_hash }
        }
@@ -614,7 +615,6 @@ impl AsRef<TaggedHash> for UnsignedBolt12Invoice {
 /// [`Refund`]: crate::offers::refund::Refund
 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
 #[derive(Clone, Debug)]
-#[cfg_attr(test, derive(PartialEq))]
 pub struct Bolt12Invoice {
        bytes: Vec<u8>,
        contents: InvoiceContents,
@@ -697,7 +697,7 @@ macro_rules! invoice_accessors { ($self: ident, $contents: expr) => {
        ///
        /// [`Offer`]: crate::offers::offer::Offer
        /// [`Offer::amount`]: crate::offers::offer::Offer::amount
-       pub fn amount(&$self) -> Option<&Amount> {
+       pub fn amount(&$self) -> Option<Amount> {
                $contents.amount()
        }
 
@@ -717,7 +717,7 @@ macro_rules! invoice_accessors { ($self: ident, $contents: expr) => {
        /// From [`Offer::description`] or [`Refund::description`].
        ///
        /// [`Offer::description`]: crate::offers::offer::Offer::description
-       pub fn description(&$self) -> PrintableString {
+       pub fn description(&$self) -> Option<PrintableString> {
                $contents.description()
        }
 
@@ -886,6 +886,20 @@ impl Bolt12Invoice {
        }
 }
 
+impl PartialEq for Bolt12Invoice {
+       fn eq(&self, other: &Self) -> bool {
+               self.bytes.eq(&other.bytes)
+       }
+}
+
+impl Eq for Bolt12Invoice {}
+
+impl Hash for Bolt12Invoice {
+       fn hash<H: Hasher>(&self, state: &mut H) {
+               self.bytes.hash(state);
+       }
+}
+
 impl InvoiceContents {
        /// Whether the original offer or refund has expired.
        #[cfg(feature = "std")]
@@ -930,7 +944,7 @@ impl InvoiceContents {
                }
        }
 
-       fn amount(&self) -> Option<&Amount> {
+       fn amount(&self) -> Option<Amount> {
                match self {
                        InvoiceContents::ForOffer { invoice_request, .. } =>
                                invoice_request.inner.offer.amount(),
@@ -938,12 +952,12 @@ impl InvoiceContents {
                }
        }
 
-       fn description(&self) -> PrintableString {
+       fn description(&self) -> Option<PrintableString> {
                match self {
                        InvoiceContents::ForOffer { invoice_request, .. } => {
                                invoice_request.inner.offer.description()
                        },
-                       InvoiceContents::ForRefund { refund, .. } => refund.description(),
+                       InvoiceContents::ForRefund { refund, .. } => Some(refund.description()),
                }
        }
 
@@ -1079,8 +1093,8 @@ impl InvoiceContents {
                                Err(_) => return None,
                        };
 
-                       let program = &address.program;
-                       let witness_program = match WitnessProgram::new(version, program.clone()) {
+                       let program = address.program.clone();
+                       let witness_program = match WitnessProgram::new(version, program) {
                                Ok(witness_program) => witness_program,
                                Err(_) => return None,
                        };
@@ -1210,7 +1224,7 @@ impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
                        (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream)
                )?;
 
-               let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
+               let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
 
                Ok(UnsignedBolt12Invoice { bytes, contents, tagged_hash })
        }
@@ -1355,7 +1369,7 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
                        None => return Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSignature)),
                        Some(signature) => signature,
                };
-               let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
+               let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
                let pubkey = contents.fields().signing_pubkey;
                merkle::verify_signature(&signature, &tagged_hash, pubkey)?;
 
@@ -1420,8 +1434,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
                        features, signing_pubkey,
                };
 
-               match offer_tlv_stream.node_id {
-                       Some(expected_signing_pubkey) => {
+               match (offer_tlv_stream.node_id, &offer_tlv_stream.paths) {
+                       (Some(expected_signing_pubkey), _) => {
                                if fields.signing_pubkey != expected_signing_pubkey {
                                        return Err(Bolt12SemanticError::InvalidSigningPubkey);
                                }
@@ -1431,7 +1445,21 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
                                )?;
                                Ok(InvoiceContents::ForOffer { invoice_request, fields })
                        },
-                       None => {
+                       (None, Some(paths)) => {
+                               if !paths
+                                       .iter()
+                                       .filter_map(|path| path.blinded_hops.last())
+                                       .any(|last_hop| fields.signing_pubkey == last_hop.blinded_node_id)
+                               {
+                                       return Err(Bolt12SemanticError::InvalidSigningPubkey);
+                               }
+
+                               let invoice_request = InvoiceRequestContents::try_from(
+                                       (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
+                               )?;
+                               Ok(InvoiceContents::ForOffer { invoice_request, fields })
+                       },
+                       (None, None) => {
                                let refund = RefundContents::try_from(
                                        (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
                                )?;
@@ -1445,16 +1473,18 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
 mod tests {
        use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, FallbackAddress, FullInvoiceTlvStreamRef, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
 
+       use bitcoin::{WitnessProgram, WitnessVersion};
        use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::blockdata::script::ScriptBuf;
        use bitcoin::hashes::Hash;
-       use bitcoin::network::constants::Network;
-       use bitcoin::secp256k1::{Message, Secp256k1, XOnlyPublicKey, self};
-       use bitcoin::address::{Address, Payload, WitnessProgram, WitnessVersion};
+       use bitcoin::network::Network;
+       use bitcoin::secp256k1::{Keypair, Message, Secp256k1, SecretKey, XOnlyPublicKey, self};
+       use bitcoin::address::{Address, Payload};
        use bitcoin::key::TweakedPublicKey;
-       use core::convert::TryFrom;
+
        use core::time::Duration;
-       use crate::blinded_path::{BlindedHop, BlindedPath};
+
+       use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
        use crate::sign::KeyMaterial;
        use crate::ln::features::{Bolt12InvoiceFeatures, InvoiceRequestFeatures, OfferFeatures};
        use crate::ln::inbound_payment::ExpandedKey;
@@ -1462,6 +1492,7 @@ mod tests {
        use crate::offers::invoice_request::InvoiceRequestTlvStreamRef;
        use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
        use crate::offers::offer::{Amount, OfferTlvStreamRef, Quantity};
+       use crate::prelude::*;
        #[cfg(not(c_bindings))]
        use {
                crate::offers::offer::OfferBuilder,
@@ -1499,7 +1530,7 @@ mod tests {
                let payment_paths = payment_paths();
                let payment_hash = payment_hash();
                let now = now();
-               let unsigned_invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let unsigned_invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1515,8 +1546,8 @@ mod tests {
                assert_eq!(unsigned_invoice.payer_metadata(), &[1; 32]);
                assert_eq!(unsigned_invoice.offer_chains(), Some(vec![ChainHash::using_genesis_block(Network::Bitcoin)]));
                assert_eq!(unsigned_invoice.metadata(), None);
-               assert_eq!(unsigned_invoice.amount(), Some(&Amount::Bitcoin { amount_msats: 1000 }));
-               assert_eq!(unsigned_invoice.description(), PrintableString("foo"));
+               assert_eq!(unsigned_invoice.amount(), Some(Amount::Bitcoin { amount_msats: 1000 }));
+               assert_eq!(unsigned_invoice.description(), Some(PrintableString("")));
                assert_eq!(unsigned_invoice.offer_features(), Some(&OfferFeatures::empty()));
                assert_eq!(unsigned_invoice.absolute_expiry(), None);
                assert_eq!(unsigned_invoice.message_paths(), &[]);
@@ -1536,7 +1567,7 @@ mod tests {
                assert!(!unsigned_invoice.is_expired());
                assert_eq!(unsigned_invoice.payment_hash(), payment_hash);
                assert_eq!(unsigned_invoice.amount_msats(), 1000);
-               assert_eq!(unsigned_invoice.fallbacks(), vec![]);
+               assert!(unsigned_invoice.fallbacks().is_empty());
                assert_eq!(unsigned_invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
                assert_eq!(unsigned_invoice.signing_pubkey(), recipient_pubkey());
 
@@ -1559,8 +1590,8 @@ mod tests {
                assert_eq!(invoice.payer_metadata(), &[1; 32]);
                assert_eq!(invoice.offer_chains(), Some(vec![ChainHash::using_genesis_block(Network::Bitcoin)]));
                assert_eq!(invoice.metadata(), None);
-               assert_eq!(invoice.amount(), Some(&Amount::Bitcoin { amount_msats: 1000 }));
-               assert_eq!(invoice.description(), PrintableString("foo"));
+               assert_eq!(invoice.amount(), Some(Amount::Bitcoin { amount_msats: 1000 }));
+               assert_eq!(invoice.description(), Some(PrintableString("")));
                assert_eq!(invoice.offer_features(), Some(&OfferFeatures::empty()));
                assert_eq!(invoice.absolute_expiry(), None);
                assert_eq!(invoice.message_paths(), &[]);
@@ -1580,14 +1611,14 @@ mod tests {
                assert!(!invoice.is_expired());
                assert_eq!(invoice.payment_hash(), payment_hash);
                assert_eq!(invoice.amount_msats(), 1000);
-               assert_eq!(invoice.fallbacks(), vec![]);
+               assert!(invoice.fallbacks().is_empty());
                assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
                assert_eq!(invoice.signing_pubkey(), recipient_pubkey());
 
-               let message = TaggedHash::new(SIGNATURE_TAG, &invoice.bytes);
+               let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice.bytes);
                assert!(merkle::verify_signature(&invoice.signature, &message, recipient_pubkey()).is_ok());
 
-               let digest = Message::from_slice(&invoice.signable_hash()).unwrap();
+               let digest = Message::from_digest(invoice.signable_hash());
                let pubkey = recipient_pubkey().into();
                let secp_ctx = Secp256k1::verification_only();
                assert!(secp_ctx.verify_schnorr(&invoice.signature, &digest, &pubkey).is_ok());
@@ -1601,7 +1632,7 @@ mod tests {
                                        metadata: None,
                                        currency: None,
                                        amount: Some(1000),
-                                       description: Some(&String::from("foo")),
+                                       description: Some(&String::from("")),
                                        features: None,
                                        absolute_expiry: None,
                                        paths: None,
@@ -1616,6 +1647,7 @@ mod tests {
                                        quantity: None,
                                        payer_id: Some(&payer_pubkey()),
                                        payer_note: None,
+                                       paths: None,
                                },
                                InvoiceTlvStreamRef {
                                        paths: Some(Iterable(payment_paths.iter().map(|(_, path)| path))),
@@ -1642,7 +1674,7 @@ mod tests {
                let payment_paths = payment_paths();
                let payment_hash = payment_hash();
                let now = now();
-               let invoice = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let invoice = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .build().unwrap()
                        .respond_with_no_std(payment_paths.clone(), payment_hash, recipient_pubkey(), now)
                        .unwrap()
@@ -1657,7 +1689,7 @@ mod tests {
                assert_eq!(invoice.offer_chains(), None);
                assert_eq!(invoice.metadata(), None);
                assert_eq!(invoice.amount(), None);
-               assert_eq!(invoice.description(), PrintableString("foo"));
+               assert_eq!(invoice.description(), Some(PrintableString("")));
                assert_eq!(invoice.offer_features(), None);
                assert_eq!(invoice.absolute_expiry(), None);
                assert_eq!(invoice.message_paths(), &[]);
@@ -1677,11 +1709,11 @@ mod tests {
                assert!(!invoice.is_expired());
                assert_eq!(invoice.payment_hash(), payment_hash);
                assert_eq!(invoice.amount_msats(), 1000);
-               assert_eq!(invoice.fallbacks(), vec![]);
+               assert!(invoice.fallbacks().is_empty());
                assert_eq!(invoice.invoice_features(), &Bolt12InvoiceFeatures::empty());
                assert_eq!(invoice.signing_pubkey(), recipient_pubkey());
 
-               let message = TaggedHash::new(SIGNATURE_TAG, &invoice.bytes);
+               let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice.bytes);
                assert!(merkle::verify_signature(&invoice.signature, &message, recipient_pubkey()).is_ok());
 
                assert_eq!(
@@ -1693,7 +1725,7 @@ mod tests {
                                        metadata: None,
                                        currency: None,
                                        amount: None,
-                                       description: Some(&String::from("foo")),
+                                       description: Some(&String::from("")),
                                        features: None,
                                        absolute_expiry: None,
                                        paths: None,
@@ -1708,6 +1740,7 @@ mod tests {
                                        quantity: None,
                                        payer_id: Some(&payer_pubkey()),
                                        payer_note: None,
+                                       paths: None,
                                },
                                InvoiceTlvStreamRef {
                                        paths: Some(Iterable(payment_paths.iter().map(|(_, path)| path))),
@@ -1735,7 +1768,7 @@ mod tests {
                let future_expiry = Duration::from_secs(u64::max_value());
                let past_expiry = Duration::from_secs(0);
 
-               if let Err(e) = OfferBuilder::new("foo".into(), recipient_pubkey())
+               if let Err(e) = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .absolute_expiry(future_expiry)
                        .build().unwrap()
@@ -1749,7 +1782,7 @@ mod tests {
                        panic!("error building invoice: {:?}", e);
                }
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .absolute_expiry(past_expiry)
                        .build().unwrap()
@@ -1771,7 +1804,7 @@ mod tests {
                let future_expiry = Duration::from_secs(u64::max_value());
                let past_expiry = Duration::from_secs(0);
 
-               if let Err(e) = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               if let Err(e) = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .absolute_expiry(future_expiry)
                        .build().unwrap()
                        .respond_with(payment_paths(), payment_hash(), recipient_pubkey())
@@ -1781,7 +1814,7 @@ mod tests {
                        panic!("error building invoice: {:?}", e);
                }
 
-               match RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               match RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .absolute_expiry(past_expiry)
                        .build().unwrap()
                        .respond_with(payment_paths(), payment_hash(), recipient_pubkey())
@@ -1795,14 +1828,13 @@ mod tests {
 
        #[test]
        fn builds_invoice_from_offer_using_derived_keys() {
-               let desc = "foo".to_string();
                let node_id = recipient_pubkey();
                let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
                let entropy = FixedEntropy {};
                let secp_ctx = Secp256k1::new();
 
                let blinded_path = BlindedPath {
-                       introduction_node_id: pubkey(40),
+                       introduction_node: IntroductionNode::NodeId(pubkey(40)),
                        blinding_point: pubkey(41),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] },
@@ -1813,7 +1845,7 @@ mod tests {
                #[cfg(c_bindings)]
                use crate::offers::offer::OfferWithDerivedMetadataBuilder as OfferBuilder;
                let offer = OfferBuilder
-                       ::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
+                       ::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
                        .amount_msats(1000)
                        .path(blinded_path)
                        .build().unwrap();
@@ -1832,9 +1864,8 @@ mod tests {
                let expanded_key = ExpandedKey::new(&KeyMaterial([41; 32]));
                assert!(invoice_request.verify(&expanded_key, &secp_ctx).is_err());
 
-               let desc = "foo".to_string();
                let offer = OfferBuilder
-                       ::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
+                       ::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
                        .amount_msats(1000)
                        // Omit the path so that node_id is used for the signing pubkey instead of deriving
                        .build().unwrap();
@@ -1857,7 +1888,7 @@ mod tests {
                let entropy = FixedEntropy {};
                let secp_ctx = Secp256k1::new();
 
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .build().unwrap();
 
                if let Err(e) = refund
@@ -1876,7 +1907,7 @@ mod tests {
                let now = now();
                let one_hour = Duration::from_secs(3600);
 
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1892,7 +1923,7 @@ mod tests {
                assert_eq!(invoice.relative_expiry(), one_hour);
                assert_eq!(tlv_stream.relative_expiry, Some(one_hour.as_secs() as u32));
 
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1911,7 +1942,7 @@ mod tests {
 
        #[test]
        fn builds_invoice_with_amount_from_request() {
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1928,7 +1959,7 @@ mod tests {
 
        #[test]
        fn builds_invoice_with_quantity_from_request() {
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Unbounded)
                        .build().unwrap()
@@ -1943,7 +1974,7 @@ mod tests {
                assert_eq!(invoice.amount_msats(), 2000);
                assert_eq!(tlv_stream.amount, Some(2000));
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Unbounded)
                        .build().unwrap()
@@ -1965,7 +1996,7 @@ mod tests {
                let x_only_pubkey = XOnlyPublicKey::from_keypair(&recipient_keys()).0;
                let tweaked_pubkey = TweakedPublicKey::dangerous_assume_tweaked(x_only_pubkey);
 
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2010,7 +2041,7 @@ mod tests {
                let mut features = Bolt12InvoiceFeatures::empty();
                features.set_basic_mpp_optional();
 
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2027,7 +2058,7 @@ mod tests {
 
        #[test]
        fn fails_signing_invoice() {
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2041,7 +2072,7 @@ mod tests {
                        Err(e) => assert_eq!(e, SignError::Signing),
                }
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2058,7 +2089,7 @@ mod tests {
 
        #[test]
        fn parses_invoice_with_payment_paths() {
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2113,7 +2144,7 @@ mod tests {
 
        #[test]
        fn parses_invoice_with_created_at() {
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2143,7 +2174,7 @@ mod tests {
 
        #[test]
        fn parses_invoice_with_relative_expiry() {
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2165,7 +2196,7 @@ mod tests {
 
        #[test]
        fn parses_invoice_with_payment_hash() {
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2195,7 +2226,7 @@ mod tests {
 
        #[test]
        fn parses_invoice_with_amount() {
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2223,7 +2254,7 @@ mod tests {
 
        #[test]
        fn parses_invoice_with_allow_mpp() {
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2254,7 +2285,7 @@ mod tests {
                let x_only_pubkey = XOnlyPublicKey::from_keypair(&recipient_keys()).0;
                let tweaked_pubkey = TweakedPublicKey::dangerous_assume_tweaked(x_only_pubkey);
 
-               let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let offer = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap();
                let invoice_request = offer
@@ -2309,7 +2340,7 @@ mod tests {
 
        #[test]
        fn parses_invoice_with_node_id() {
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2348,10 +2379,85 @@ mod tests {
                }
        }
 
+       #[test]
+       fn parses_invoice_with_node_id_from_blinded_path() {
+               let paths = vec![
+                       BlindedPath {
+                               introduction_node: IntroductionNode::NodeId(pubkey(40)),
+                               blinding_point: pubkey(41),
+                               blinded_hops: vec![
+                                       BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
+                                       BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
+                               ],
+                       },
+                       BlindedPath {
+                               introduction_node: IntroductionNode::NodeId(pubkey(40)),
+                               blinding_point: pubkey(41),
+                               blinded_hops: vec![
+                                       BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
+                                       BlindedHop { blinded_node_id: pubkey(46), encrypted_payload: vec![0; 46] },
+                               ],
+                       },
+               ];
+
+               let blinded_node_id_sign = |message: &UnsignedBolt12Invoice| {
+                       let secp_ctx = Secp256k1::new();
+                       let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[46; 32]).unwrap());
+                       Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+               };
+
+               let invoice = OfferBuilder::new(recipient_pubkey())
+                       .clear_signing_pubkey()
+                       .amount_msats(1000)
+                       .path(paths[0].clone())
+                       .path(paths[1].clone())
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap()
+                       .respond_with_no_std_using_signing_pubkey(
+                               payment_paths(), payment_hash(), now(), pubkey(46)
+                       ).unwrap()
+                       .build().unwrap()
+                       .sign(blinded_node_id_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice.write(&mut buffer).unwrap();
+
+               if let Err(e) = Bolt12Invoice::try_from(buffer) {
+                       panic!("error parsing invoice: {:?}", e);
+               }
+
+               let invoice = OfferBuilder::new(recipient_pubkey())
+                       .clear_signing_pubkey()
+                       .amount_msats(1000)
+                       .path(paths[0].clone())
+                       .path(paths[1].clone())
+                       .build().unwrap()
+                       .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap()
+                       .respond_with_no_std_using_signing_pubkey(
+                               payment_paths(), payment_hash(), now(), recipient_pubkey()
+                       ).unwrap()
+                       .build().unwrap()
+                       .sign(recipient_sign).unwrap();
+
+               let mut buffer = Vec::new();
+               invoice.write(&mut buffer).unwrap();
+
+               match Bolt12Invoice::try_from(buffer) {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => {
+                               assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidSigningPubkey));
+                       },
+               }
+       }
+
        #[test]
        fn fails_parsing_invoice_without_signature() {
                let mut buffer = Vec::new();
-               OfferBuilder::new("foo".into(), recipient_pubkey())
+               OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2370,7 +2476,7 @@ mod tests {
 
        #[test]
        fn fails_parsing_invoice_with_invalid_signature() {
-               let mut invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let mut invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2395,7 +2501,7 @@ mod tests {
 
        #[test]
        fn fails_parsing_invoice_with_extra_tlv_records() {
-               let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
index 5476ad551b7b627d5e1abac8d19fc836b673e54c..5ae5f457a84822172d970035286a562be0d67973 100644 (file)
 
 use crate::io;
 use crate::ln::msgs::DecodeError;
+use crate::offers::merkle::SignError;
 use crate::offers::parse::Bolt12SemanticError;
 use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
 use crate::util::string::UntrustedString;
 
+#[allow(unused_imports)]
 use crate::prelude::*;
 
 /// An error in response to an [`InvoiceRequest`] or an [`Bolt12Invoice`].
@@ -112,6 +114,19 @@ impl From<Bolt12SemanticError> for InvoiceError {
        }
 }
 
+impl From<SignError> for InvoiceError {
+       fn from(error: SignError) -> Self {
+               let message = match error {
+                       SignError::Signing => "Failed signing invoice",
+                       SignError::Verification(_) => "Failed invoice signature verification",
+               };
+               InvoiceError {
+                       erroneous_field: None,
+                       message: UntrustedString(message.to_string()),
+               }
+       }
+}
+
 #[cfg(test)]
 mod tests {
        use super::{ErroneousField, InvoiceError};
index f282075d9333cf1f9002154519474518036b9336..cdf94a2e80d5bee168deea35fdb03c817718cdc7 100644 (file)
@@ -23,8 +23,8 @@
 //! extern crate bitcoin;
 //! extern crate lightning;
 //!
-//! use bitcoin::network::constants::Network;
-//! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
+//! use bitcoin::network::Network;
+//! use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, SecretKey};
 //! use lightning::ln::features::OfferFeatures;
 //! use lightning::offers::invoice_request::UnsignedInvoiceRequest;
 //! use lightning::offers::offer::Offer;
@@ -32,7 +32,7 @@
 //!
 //! # fn parse() -> Result<(), lightning::offers::parse::Bolt12ParseError> {
 //! let secp_ctx = Secp256k1::new();
-//! let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32])?);
+//! let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32])?);
 //! let pubkey = PublicKey::from(keys);
 //! let mut buffer = Vec::new();
 //!
 //! ```
 
 use bitcoin::blockdata::constants::ChainHash;
-use bitcoin::network::constants::Network;
-use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, self};
+use bitcoin::network::Network;
+use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, self};
 use bitcoin::secp256k1::schnorr::Signature;
-use core::convert::{AsRef, TryFrom};
 use core::ops::Deref;
 use crate::sign::EntropySource;
 use crate::io;
 use crate::blinded_path::BlindedPath;
-use crate::ln::PaymentHash;
+use crate::ln::types::PaymentHash;
 use crate::ln::channelmanager::PaymentId;
 use crate::ln::features::InvoiceRequestFeatures;
 use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
 use crate::ln::msgs::DecodeError;
 use crate::offers::invoice::BlindedPayInfo;
 use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, self};
-use crate::offers::offer::{Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef};
+use crate::offers::offer::{Offer, OfferContents, OfferId, OfferTlvStream, OfferTlvStreamRef};
 use crate::offers::parse::{Bolt12ParseError, ParsedMessage, Bolt12SemanticError};
 use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
 use crate::offers::signer::{Metadata, MetadataMaterial};
-use crate::util::ser::{HighZeroBytesDroppedBigSize, SeekReadable, WithoutLength, Writeable, Writer};
-use crate::util::string::PrintableString;
+use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, SeekReadable, WithoutLength, Writeable, Writer};
+use crate::util::string::{PrintableString, UntrustedString};
 
 #[cfg(not(c_bindings))]
 use {
@@ -89,6 +88,7 @@ use {
        crate::offers::invoice::{InvoiceWithDerivedSigningPubkeyBuilder, InvoiceWithExplicitSigningPubkeyBuilder},
 };
 
+#[allow(unused_imports)]
 use crate::prelude::*;
 
 /// Tag for the hash function used when signing an [`InvoiceRequest`]'s merkle root.
@@ -303,7 +303,7 @@ macro_rules! invoice_request_builder_methods { (
        }
 
        fn build_with_checks($($self_mut)* $self: $self_type) -> Result<
-               (UnsignedInvoiceRequest, Option<KeyPair>, Option<&'b Secp256k1<$secp_context>>),
+               (UnsignedInvoiceRequest, Option<Keypair>, Option<&'b Secp256k1<$secp_context>>),
                Bolt12SemanticError
        > {
                #[cfg(feature = "std")] {
@@ -334,7 +334,7 @@ macro_rules! invoice_request_builder_methods { (
        }
 
        fn build_without_checks($($self_mut)* $self: $self_type) ->
-               (UnsignedInvoiceRequest, Option<KeyPair>, Option<&'b Secp256k1<$secp_context>>)
+               (UnsignedInvoiceRequest, Option<Keypair>, Option<&'b Secp256k1<$secp_context>>)
        {
                // Create the metadata for stateless verification of a Bolt12Invoice.
                let mut keys = None;
@@ -487,6 +487,7 @@ for InvoiceRequestBuilder<'a, 'b, DerivedPayerId, secp256k1::All> {
 ///
 /// This is serialized as a TLV stream, which includes TLV records from the originating message. As
 /// such, it may include unknown, odd TLV records.
+#[derive(Clone)]
 pub struct UnsignedInvoiceRequest {
        bytes: Vec<u8>,
        contents: InvoiceRequestContents,
@@ -529,7 +530,7 @@ impl UnsignedInvoiceRequest {
                let mut bytes = Vec::new();
                unsigned_tlv_stream.write(&mut bytes).unwrap();
 
-               let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
+               let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
 
                Self { bytes, contents, tagged_hash }
        }
@@ -607,6 +608,9 @@ pub struct InvoiceRequest {
 /// ways to respond depending on whether the signing keys were derived.
 #[derive(Clone, Debug)]
 pub struct VerifiedInvoiceRequest {
+       /// The identifier of the [`Offer`] for which the [`InvoiceRequest`] was made.
+       pub offer_id: OfferId,
+
        /// The verified request.
        inner: InvoiceRequest,
 
@@ -618,7 +622,7 @@ pub struct VerifiedInvoiceRequest {
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        /// [`respond_using_derived_keys`]: Self::respond_using_derived_keys
        /// [`respond_with`]: Self::respond_with
-       pub keys: Option<KeyPair>,
+       pub keys: Option<Keypair>,
 }
 
 /// The contents of an [`InvoiceRequest`], which may be shared with an [`Bolt12Invoice`].
@@ -744,7 +748,27 @@ macro_rules! invoice_request_respond_with_explicit_signing_pubkey_methods { (
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
 
-               <$builder>::for_offer(&$contents, payment_paths, created_at, payment_hash)
+               let signing_pubkey = match $contents.contents.inner.offer.signing_pubkey() {
+                       Some(signing_pubkey) => signing_pubkey,
+                       None => return Err(Bolt12SemanticError::MissingSigningPubkey),
+               };
+
+               <$builder>::for_offer(&$contents, payment_paths, created_at, payment_hash, signing_pubkey)
+       }
+
+       #[cfg(test)]
+       #[allow(dead_code)]
+       pub(super) fn respond_with_no_std_using_signing_pubkey(
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
+               created_at: core::time::Duration, signing_pubkey: PublicKey
+       ) -> Result<$builder, Bolt12SemanticError> {
+               debug_assert!($contents.contents.inner.offer.signing_pubkey().is_none());
+
+               if $contents.invoice_request_features().requires_unknown_bits() {
+                       return Err(Bolt12SemanticError::UnknownRequiredFeatures);
+               }
+
+               <$builder>::for_offer(&$contents, payment_paths, created_at, payment_hash, signing_pubkey)
        }
 } }
 
@@ -764,8 +788,9 @@ macro_rules! invoice_request_verify_method { ($self: ident, $self_type: ty) => {
                #[cfg(c_bindings)]
                secp_ctx: &Secp256k1<secp256k1::All>,
        ) -> Result<VerifiedInvoiceRequest, ()> {
-               let keys = $self.contents.inner.offer.verify(&$self.bytes, key, secp_ctx)?;
+               let (offer_id, keys) = $self.contents.inner.offer.verify(&$self.bytes, key, secp_ctx)?;
                Ok(VerifiedInvoiceRequest {
+                       offer_id,
                        #[cfg(not(c_bindings))]
                        inner: $self,
                        #[cfg(c_bindings)]
@@ -851,6 +876,11 @@ macro_rules! invoice_request_respond_with_derived_signing_pubkey_methods { (
                        Some(keys) => keys,
                };
 
+               match $contents.contents.inner.offer.signing_pubkey() {
+                       Some(signing_pubkey) => debug_assert_eq!(signing_pubkey, keys.public_key()),
+                       None => return Err(Bolt12SemanticError::MissingSigningPubkey),
+               }
+
                <$builder>::for_offer_using_keys(
                        &$self.inner, payment_paths, created_at, payment_hash, keys
                )
@@ -868,6 +898,22 @@ impl VerifiedInvoiceRequest {
        invoice_request_respond_with_derived_signing_pubkey_methods!(self, self.inner, InvoiceBuilder<DerivedSigningPubkey>);
        #[cfg(c_bindings)]
        invoice_request_respond_with_derived_signing_pubkey_methods!(self, self.inner, InvoiceWithDerivedSigningPubkeyBuilder);
+
+       pub(crate) fn fields(&self) -> InvoiceRequestFields {
+               let InvoiceRequestContents {
+                       payer_id,
+                       inner: InvoiceRequestContentsWithoutPayerId {
+                               payer: _, offer: _, chain: _, amount_msats: _, features: _, quantity, payer_note
+                       },
+               } = &self.inner.contents;
+
+               InvoiceRequestFields {
+                       payer_id: *payer_id,
+                       quantity: *quantity,
+                       payer_note_truncated: payer_note.clone()
+                               .map(|mut s| { s.truncate(PAYER_NOTE_LIMIT); UntrustedString(s) }),
+               }
+       }
 }
 
 impl InvoiceRequestContents {
@@ -939,6 +985,7 @@ impl InvoiceRequestContentsWithoutPayerId {
                        quantity: self.quantity,
                        payer_id: None,
                        payer_note: self.payer_note.as_ref(),
+                       paths: None,
                };
 
                (payer, offer, invoice_request)
@@ -971,6 +1018,8 @@ pub(super) const INVOICE_REQUEST_TYPES: core::ops::Range<u64> = 80..160;
 /// [`Refund::payer_id`]: crate::offers::refund::Refund::payer_id
 pub(super) const INVOICE_REQUEST_PAYER_ID_TYPE: u64 = 88;
 
+// This TLV stream is used for both InvoiceRequest and Refund, but not all TLV records are valid for
+// InvoiceRequest as noted below.
 tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef, INVOICE_REQUEST_TYPES, {
        (80, chain: ChainHash),
        (82, amount: (u64, HighZeroBytesDroppedBigSize)),
@@ -978,6 +1027,8 @@ tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef, INVOICE_REQUEST
        (86, quantity: (u64, HighZeroBytesDroppedBigSize)),
        (INVOICE_REQUEST_PAYER_ID_TYPE, payer_id: PublicKey),
        (89, payer_note: (String, WithoutLength)),
+       // Only used for Refund since the onion message of an InvoiceRequest has a reply path.
+       (90, paths: (Vec<BlindedPath>, WithoutLength)),
 });
 
 type FullInvoiceRequestTlvStream =
@@ -1022,7 +1073,7 @@ impl TryFrom<Vec<u8>> for UnsignedInvoiceRequest {
                        (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
                )?;
 
-               let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
+               let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
 
                Ok(UnsignedInvoiceRequest { bytes, contents, tagged_hash })
        }
@@ -1046,7 +1097,7 @@ impl TryFrom<Vec<u8>> for InvoiceRequest {
                        None => return Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSignature)),
                        Some(signature) => signature,
                };
-               let message = TaggedHash::new(SIGNATURE_TAG, &bytes);
+               let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
                merkle::verify_signature(&signature, &message, contents.payer_id)?;
 
                Ok(InvoiceRequest { bytes, contents, signature })
@@ -1060,7 +1111,9 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
                let (
                        PayerTlvStream { metadata },
                        offer_tlv_stream,
-                       InvoiceRequestTlvStream { chain, amount, features, quantity, payer_id, payer_note },
+                       InvoiceRequestTlvStream {
+                               chain, amount, features, quantity, payer_id, payer_note, paths,
+                       },
                ) = tlv_stream;
 
                let payer = match metadata {
@@ -1087,6 +1140,10 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
                        Some(payer_id) => payer_id,
                };
 
+               if paths.is_some() {
+                       return Err(Bolt12SemanticError::UnexpectedPaths);
+               }
+
                Ok(InvoiceRequestContents {
                        inner: InvoiceRequestContentsWithoutPayerId {
                                payer, offer, chain, amount_msats: amount, features, quantity, payer_note,
@@ -1096,14 +1153,59 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
        }
 }
 
+/// Fields sent in an [`InvoiceRequest`] message to include in [`PaymentContext::Bolt12Offer`].
+///
+/// [`PaymentContext::Bolt12Offer`]: crate::blinded_path::payment::PaymentContext::Bolt12Offer
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct InvoiceRequestFields {
+       /// A possibly transient pubkey used to sign the invoice request.
+       pub payer_id: PublicKey,
+
+       /// The quantity of the offer's item conforming to [`Offer::is_valid_quantity`].
+       pub quantity: Option<u64>,
+
+       /// A payer-provided note which will be seen by the recipient and reflected back in the invoice
+       /// response. Truncated to [`PAYER_NOTE_LIMIT`] characters.
+       pub payer_note_truncated: Option<UntrustedString>,
+}
+
+/// The maximum number of characters included in [`InvoiceRequestFields::payer_note_truncated`].
+pub const PAYER_NOTE_LIMIT: usize = 512;
+
+impl Writeable for InvoiceRequestFields {
+       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+               write_tlv_fields!(writer, {
+                       (0, self.payer_id, required),
+                       (2, self.quantity.map(|v| HighZeroBytesDroppedBigSize(v)), option),
+                       (4, self.payer_note_truncated.as_ref().map(|s| WithoutLength(&s.0)), option),
+               });
+               Ok(())
+       }
+}
+
+impl Readable for InvoiceRequestFields {
+       fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
+               _init_and_read_len_prefixed_tlv_fields!(reader, {
+                       (0, payer_id, required),
+                       (2, quantity, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
+                       (4, payer_note_truncated, (option, encoding: (String, WithoutLength))),
+               });
+
+               Ok(InvoiceRequestFields {
+                       payer_id: payer_id.0.unwrap(),
+                       quantity,
+                       payer_note_truncated: payer_note_truncated.map(|s| UntrustedString(s)),
+               })
+       }
+}
+
 #[cfg(test)]
 mod tests {
-       use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG, UnsignedInvoiceRequest};
+       use super::{InvoiceRequest, InvoiceRequestFields, InvoiceRequestTlvStreamRef, PAYER_NOTE_LIMIT, SIGNATURE_TAG, UnsignedInvoiceRequest};
 
        use bitcoin::blockdata::constants::ChainHash;
-       use bitcoin::network::constants::Network;
-       use bitcoin::secp256k1::{KeyPair, Secp256k1, SecretKey, self};
-       use core::convert::TryFrom;
+       use bitcoin::network::Network;
+       use bitcoin::secp256k1::{Keypair, Secp256k1, SecretKey, self};
        use core::num::NonZeroU64;
        #[cfg(feature = "std")]
        use core::time::Duration;
@@ -1126,12 +1228,12 @@ mod tests {
        use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
        use crate::offers::payer::PayerTlvStreamRef;
        use crate::offers::test_utils::*;
-       use crate::util::ser::{BigSize, Writeable};
-       use crate::util::string::PrintableString;
+       use crate::util::ser::{BigSize, Readable, Writeable};
+       use crate::util::string::{PrintableString, UntrustedString};
 
        #[test]
        fn builds_invoice_request_with_defaults() {
-               let unsigned_invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let unsigned_invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1146,14 +1248,14 @@ mod tests {
                assert_eq!(unsigned_invoice_request.payer_metadata(), &[1; 32]);
                assert_eq!(unsigned_invoice_request.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]);
                assert_eq!(unsigned_invoice_request.metadata(), None);
-               assert_eq!(unsigned_invoice_request.amount(), Some(&Amount::Bitcoin { amount_msats: 1000 }));
-               assert_eq!(unsigned_invoice_request.description(), PrintableString("foo"));
+               assert_eq!(unsigned_invoice_request.amount(), Some(Amount::Bitcoin { amount_msats: 1000 }));
+               assert_eq!(unsigned_invoice_request.description(), Some(PrintableString("")));
                assert_eq!(unsigned_invoice_request.offer_features(), &OfferFeatures::empty());
                assert_eq!(unsigned_invoice_request.absolute_expiry(), None);
                assert_eq!(unsigned_invoice_request.paths(), &[]);
                assert_eq!(unsigned_invoice_request.issuer(), None);
                assert_eq!(unsigned_invoice_request.supported_quantity(), Quantity::One);
-               assert_eq!(unsigned_invoice_request.signing_pubkey(), recipient_pubkey());
+               assert_eq!(unsigned_invoice_request.signing_pubkey(), Some(recipient_pubkey()));
                assert_eq!(unsigned_invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
                assert_eq!(unsigned_invoice_request.amount_msats(), None);
                assert_eq!(unsigned_invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty());
@@ -1178,14 +1280,14 @@ mod tests {
                assert_eq!(invoice_request.payer_metadata(), &[1; 32]);
                assert_eq!(invoice_request.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]);
                assert_eq!(invoice_request.metadata(), None);
-               assert_eq!(invoice_request.amount(), Some(&Amount::Bitcoin { amount_msats: 1000 }));
-               assert_eq!(invoice_request.description(), PrintableString("foo"));
+               assert_eq!(invoice_request.amount(), Some(Amount::Bitcoin { amount_msats: 1000 }));
+               assert_eq!(invoice_request.description(), Some(PrintableString("")));
                assert_eq!(invoice_request.offer_features(), &OfferFeatures::empty());
                assert_eq!(invoice_request.absolute_expiry(), None);
                assert_eq!(invoice_request.paths(), &[]);
                assert_eq!(invoice_request.issuer(), None);
                assert_eq!(invoice_request.supported_quantity(), Quantity::One);
-               assert_eq!(invoice_request.signing_pubkey(), recipient_pubkey());
+               assert_eq!(invoice_request.signing_pubkey(), Some(recipient_pubkey()));
                assert_eq!(invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin));
                assert_eq!(invoice_request.amount_msats(), None);
                assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty());
@@ -1193,7 +1295,7 @@ mod tests {
                assert_eq!(invoice_request.payer_id(), payer_pubkey());
                assert_eq!(invoice_request.payer_note(), None);
 
-               let message = TaggedHash::new(SIGNATURE_TAG, &invoice_request.bytes);
+               let message = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &invoice_request.bytes);
                assert!(merkle::verify_signature(&invoice_request.signature, &message, payer_pubkey()).is_ok());
 
                assert_eq!(
@@ -1205,7 +1307,7 @@ mod tests {
                                        metadata: None,
                                        currency: None,
                                        amount: Some(1000),
-                                       description: Some(&String::from("foo")),
+                                       description: Some(&String::from("")),
                                        features: None,
                                        absolute_expiry: None,
                                        paths: None,
@@ -1220,6 +1322,7 @@ mod tests {
                                        quantity: None,
                                        payer_id: Some(&payer_pubkey()),
                                        payer_note: None,
+                                       paths: None,
                                },
                                SignatureTlvStreamRef { signature: Some(&invoice_request.signature()) },
                        ),
@@ -1236,7 +1339,7 @@ mod tests {
                let future_expiry = Duration::from_secs(u64::max_value());
                let past_expiry = Duration::from_secs(0);
 
-               if let Err(e) = OfferBuilder::new("foo".into(), recipient_pubkey())
+               if let Err(e) = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .absolute_expiry(future_expiry)
                        .build().unwrap()
@@ -1246,7 +1349,7 @@ mod tests {
                        panic!("error building invoice_request: {:?}", e);
                }
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .absolute_expiry(past_expiry)
                        .build().unwrap()
@@ -1266,7 +1369,7 @@ mod tests {
                let secp_ctx = Secp256k1::new();
                let payment_id = PaymentId([1; 32]);
 
-               let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let offer = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap();
                let invoice_request = offer
@@ -1298,7 +1401,7 @@ mod tests {
                let mut bytes = Vec::new();
                tlv_stream.write(&mut bytes).unwrap();
 
-               let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes);
+               let message = TaggedHash::from_valid_tlv_stream_bytes(INVOICE_SIGNATURE_TAG, &bytes);
                let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap();
                signature_tlv_stream.signature = Some(&signature);
 
@@ -1321,7 +1424,7 @@ mod tests {
                let mut bytes = Vec::new();
                tlv_stream.write(&mut bytes).unwrap();
 
-               let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes);
+               let message = TaggedHash::from_valid_tlv_stream_bytes(INVOICE_SIGNATURE_TAG, &bytes);
                let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap();
                signature_tlv_stream.signature = Some(&signature);
 
@@ -1339,7 +1442,7 @@ mod tests {
                let secp_ctx = Secp256k1::new();
                let payment_id = PaymentId([1; 32]);
 
-               let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let offer = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap();
                let invoice_request = offer
@@ -1370,7 +1473,7 @@ mod tests {
                let mut bytes = Vec::new();
                tlv_stream.write(&mut bytes).unwrap();
 
-               let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes);
+               let message = TaggedHash::from_valid_tlv_stream_bytes(INVOICE_SIGNATURE_TAG, &bytes);
                let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap();
                signature_tlv_stream.signature = Some(&signature);
 
@@ -1393,7 +1496,7 @@ mod tests {
                let mut bytes = Vec::new();
                tlv_stream.write(&mut bytes).unwrap();
 
-               let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes);
+               let message = TaggedHash::from_valid_tlv_stream_bytes(INVOICE_SIGNATURE_TAG, &bytes);
                let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap();
                signature_tlv_stream.signature = Some(&signature);
 
@@ -1409,7 +1512,7 @@ mod tests {
                let mainnet = ChainHash::using_genesis_block(Network::Bitcoin);
                let testnet = ChainHash::using_genesis_block(Network::Testnet);
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1420,7 +1523,7 @@ mod tests {
                assert_eq!(invoice_request.chain(), mainnet);
                assert_eq!(tlv_stream.chain, None);
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .chain(Network::Testnet)
                        .build().unwrap()
@@ -1432,7 +1535,7 @@ mod tests {
                assert_eq!(invoice_request.chain(), testnet);
                assert_eq!(tlv_stream.chain, Some(&testnet));
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .chain(Network::Bitcoin)
                        .chain(Network::Testnet)
@@ -1445,7 +1548,7 @@ mod tests {
                assert_eq!(invoice_request.chain(), mainnet);
                assert_eq!(tlv_stream.chain, None);
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .chain(Network::Bitcoin)
                        .chain(Network::Testnet)
@@ -1459,7 +1562,7 @@ mod tests {
                assert_eq!(invoice_request.chain(), testnet);
                assert_eq!(tlv_stream.chain, Some(&testnet));
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .chain(Network::Testnet)
                        .build().unwrap()
@@ -1470,7 +1573,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12SemanticError::UnsupportedChain),
                }
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .chain(Network::Testnet)
                        .build().unwrap()
@@ -1484,7 +1587,7 @@ mod tests {
 
        #[test]
        fn builds_invoice_request_with_amount() {
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1495,7 +1598,7 @@ mod tests {
                assert_eq!(invoice_request.amount_msats(), Some(1000));
                assert_eq!(tlv_stream.amount, Some(1000));
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1507,7 +1610,7 @@ mod tests {
                assert_eq!(invoice_request.amount_msats(), Some(1000));
                assert_eq!(tlv_stream.amount, Some(1000));
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1518,7 +1621,7 @@ mod tests {
                assert_eq!(invoice_request.amount_msats(), Some(1001));
                assert_eq!(tlv_stream.amount, Some(1001));
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1528,7 +1631,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12SemanticError::InsufficientAmount),
                }
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Unbounded)
                        .build().unwrap()
@@ -1540,7 +1643,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12SemanticError::InsufficientAmount),
                }
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1550,7 +1653,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidAmount),
                }
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Unbounded)
                        .build().unwrap()
@@ -1563,7 +1666,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12SemanticError::InsufficientAmount),
                }
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
                        .build()
@@ -1572,7 +1675,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12SemanticError::MissingAmount),
                }
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Unbounded)
                        .build().unwrap()
@@ -1587,7 +1690,7 @@ mod tests {
 
        #[test]
        fn builds_invoice_request_with_features() {
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1598,7 +1701,7 @@ mod tests {
                assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::unknown());
                assert_eq!(tlv_stream.features, Some(&InvoiceRequestFeatures::unknown()));
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1616,7 +1719,7 @@ mod tests {
                let one = NonZeroU64::new(1).unwrap();
                let ten = NonZeroU64::new(10).unwrap();
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::One)
                        .build().unwrap()
@@ -1627,7 +1730,7 @@ mod tests {
                assert_eq!(invoice_request.quantity(), None);
                assert_eq!(tlv_stream.quantity, None);
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::One)
                        .build().unwrap()
@@ -1639,7 +1742,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12SemanticError::UnexpectedQuantity),
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Bounded(ten))
                        .build().unwrap()
@@ -1652,7 +1755,7 @@ mod tests {
                assert_eq!(invoice_request.amount_msats(), Some(10_000));
                assert_eq!(tlv_stream.amount, Some(10_000));
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Bounded(ten))
                        .build().unwrap()
@@ -1664,7 +1767,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidQuantity),
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Unbounded)
                        .build().unwrap()
@@ -1677,7 +1780,7 @@ mod tests {
                assert_eq!(invoice_request.amount_msats(), Some(2_000));
                assert_eq!(tlv_stream.amount, Some(2_000));
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Unbounded)
                        .build().unwrap()
@@ -1688,7 +1791,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12SemanticError::MissingQuantity),
                }
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Bounded(one))
                        .build().unwrap()
@@ -1702,7 +1805,7 @@ mod tests {
 
        #[test]
        fn builds_invoice_request_with_payer_note() {
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1713,7 +1816,7 @@ mod tests {
                assert_eq!(invoice_request.payer_note(), Some(PrintableString("bar")));
                assert_eq!(tlv_stream.payer_note, Some(&String::from("bar")));
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1728,7 +1831,7 @@ mod tests {
 
        #[test]
        fn fails_signing_invoice_request() {
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1739,7 +1842,7 @@ mod tests {
                        Err(e) => assert_eq!(e, SignError::Signing),
                }
 
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1753,7 +1856,7 @@ mod tests {
 
        #[test]
        fn fails_responding_with_unknown_required_features() {
-               match OfferBuilder::new("foo".into(), recipient_pubkey())
+               match OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![42; 32], payer_pubkey()).unwrap()
@@ -1769,7 +1872,7 @@ mod tests {
 
        #[test]
        fn parses_invoice_request_with_metadata() {
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1786,7 +1889,7 @@ mod tests {
 
        #[test]
        fn parses_invoice_request_with_chain() {
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1801,7 +1904,7 @@ mod tests {
                        panic!("error parsing invoice_request: {:?}", e);
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1820,7 +1923,7 @@ mod tests {
 
        #[test]
        fn parses_invoice_request_with_amount() {
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1834,7 +1937,7 @@ mod tests {
                        panic!("error parsing invoice_request: {:?}", e);
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
                        .amount_msats(1000).unwrap()
@@ -1848,7 +1951,7 @@ mod tests {
                        panic!("error parsing invoice_request: {:?}", e);
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
                        .build_unchecked()
@@ -1862,7 +1965,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingAmount)),
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1878,7 +1981,8 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InsufficientAmount)),
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
+                       .description("foo".to_string())
                        .amount(Amount::Currency { iso4217_code: *b"USD", amount: 1000 })
                        .build_unchecked()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -1895,7 +1999,7 @@ mod tests {
                        },
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Unbounded)
                        .build().unwrap()
@@ -1918,7 +2022,7 @@ mod tests {
                let one = NonZeroU64::new(1).unwrap();
                let ten = NonZeroU64::new(10).unwrap();
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::One)
                        .build().unwrap()
@@ -1933,7 +2037,7 @@ mod tests {
                        panic!("error parsing invoice_request: {:?}", e);
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::One)
                        .build().unwrap()
@@ -1953,7 +2057,7 @@ mod tests {
                        },
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Bounded(ten))
                        .build().unwrap()
@@ -1970,7 +2074,7 @@ mod tests {
                        panic!("error parsing invoice_request: {:?}", e);
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Bounded(ten))
                        .build().unwrap()
@@ -1988,7 +2092,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::InvalidQuantity)),
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Unbounded)
                        .build().unwrap()
@@ -2005,7 +2109,7 @@ mod tests {
                        panic!("error parsing invoice_request: {:?}", e);
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Unbounded)
                        .build().unwrap()
@@ -2021,7 +2125,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingQuantity)),
                }
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .supported_quantity(Quantity::Bounded(one))
                        .build().unwrap()
@@ -2040,7 +2144,7 @@ mod tests {
 
        #[test]
        fn fails_parsing_invoice_request_without_metadata() {
-               let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let offer = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap();
                let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2061,7 +2165,7 @@ mod tests {
 
        #[test]
        fn fails_parsing_invoice_request_without_payer_id() {
-               let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let offer = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap();
                let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2080,7 +2184,7 @@ mod tests {
 
        #[test]
        fn fails_parsing_invoice_request_without_node_id() {
-               let offer = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let offer = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap();
                let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2102,7 +2206,7 @@ mod tests {
        #[test]
        fn fails_parsing_invoice_request_without_signature() {
                let mut buffer = Vec::new();
-               OfferBuilder::new("foo".into(), recipient_pubkey())
+               OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2118,7 +2222,7 @@ mod tests {
 
        #[test]
        fn fails_parsing_invoice_request_with_invalid_signature() {
-               let mut invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+               let mut invoice_request = OfferBuilder::new(recipient_pubkey())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -2141,8 +2245,8 @@ mod tests {
        #[test]
        fn fails_parsing_invoice_request_with_extra_tlv_records() {
                let secp_ctx = Secp256k1::new();
-               let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
-               let invoice_request = OfferBuilder::new("foo".into(), keys.public_key())
+               let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+               let invoice_request = OfferBuilder::new(keys.public_key())
                        .amount_msats(1000)
                        .build().unwrap()
                        .request_invoice(vec![1; 32], keys.public_key()).unwrap()
@@ -2163,4 +2267,51 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)),
                }
        }
+
+       #[test]
+       fn copies_verified_invoice_request_fields() {
+               let node_id = recipient_pubkey();
+               let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
+               let entropy = FixedEntropy {};
+               let secp_ctx = Secp256k1::new();
+
+               #[cfg(c_bindings)]
+               use crate::offers::offer::OfferWithDerivedMetadataBuilder as OfferBuilder;
+               let offer = OfferBuilder
+                       ::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
+                       .chain(Network::Testnet)
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::Unbounded)
+                       .build().unwrap();
+               assert_eq!(offer.signing_pubkey(), Some(node_id));
+
+               let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .chain(Network::Testnet).unwrap()
+                       .quantity(1).unwrap()
+                       .payer_note("0".repeat(PAYER_NOTE_LIMIT * 2))
+                       .build().unwrap()
+                       .sign(payer_sign).unwrap();
+               match invoice_request.verify(&expanded_key, &secp_ctx) {
+                       Ok(invoice_request) => {
+                               let fields = invoice_request.fields();
+                               assert_eq!(invoice_request.offer_id, offer.id());
+                               assert_eq!(
+                                       fields,
+                                       InvoiceRequestFields {
+                                               payer_id: payer_pubkey(),
+                                               quantity: Some(1),
+                                               payer_note_truncated: Some(UntrustedString("0".repeat(PAYER_NOTE_LIMIT))),
+                                       }
+                               );
+
+                               let mut buffer = Vec::new();
+                               fields.write(&mut buffer).unwrap();
+
+                               let deserialized_fields: InvoiceRequestFields =
+                                       Readable::read(&mut buffer.as_slice()).unwrap();
+                               assert_eq!(deserialized_fields, fields);
+                       },
+                       Err(_) => panic!("unexpected error"),
+               }
+       }
 }
index 941bf196716d8b5ee2d2e51d2c4db95716bdf869..11f1d971330c7f1c3f7447aa2f2b7ee5a5f5ddd7 100644 (file)
 use bitcoin::hashes::{Hash, HashEngine, sha256};
 use bitcoin::secp256k1::{Message, PublicKey, Secp256k1, self};
 use bitcoin::secp256k1::schnorr::Signature;
-use core::convert::AsRef;
 use crate::io;
 use crate::util::ser::{BigSize, Readable, Writeable, Writer};
 
+#[allow(unused_imports)]
 use crate::prelude::*;
 
 /// Valid type range for signature TLV records.
@@ -38,13 +38,23 @@ pub struct TaggedHash {
 }
 
 impl TaggedHash {
+       /// Creates a tagged hash with the given parameters.
+       ///
+       /// Panics if `bytes` is not a well-formed TLV stream containing at least one TLV record.
+       pub(super) fn from_valid_tlv_stream_bytes(tag: &'static str, bytes: &[u8]) -> Self {
+               let tlv_stream = TlvStream::new(bytes);
+               Self::from_tlv_stream(tag, tlv_stream)
+       }
+
        /// Creates a tagged hash with the given parameters.
        ///
        /// Panics if `tlv_stream` is not a well-formed TLV stream containing at least one TLV record.
-       pub(super) fn new(tag: &'static str, tlv_stream: &[u8]) -> Self {
+       pub(super) fn from_tlv_stream<'a, I: core::iter::Iterator<Item = TlvRecord<'a>>>(
+               tag: &'static str, tlv_stream: I
+       ) -> Self {
                let tag_hash = sha256::Hash::hash(tag.as_bytes());
                let merkle_root = root_hash(tlv_stream);
-               let digest = Message::from_slice(tagged_hash(tag_hash, merkle_root).as_byte_array()).unwrap();
+               let digest = Message::from_digest(tagged_hash(tag_hash, merkle_root).to_byte_array());
                Self {
                        tag,
                        merkle_root,
@@ -66,6 +76,10 @@ impl TaggedHash {
        pub fn merkle_root(&self) -> sha256::Hash {
                self.merkle_root
        }
+
+       pub(super) fn to_bytes(&self) -> [u8; 32] {
+               *self.digest.as_ref()
+       }
 }
 
 impl AsRef<TaggedHash> for TaggedHash {
@@ -137,9 +151,10 @@ pub(super) fn verify_signature(
 
 /// Computes a merkle root hash for the given data, which must be a well-formed TLV stream
 /// containing at least one TLV record.
-fn root_hash(data: &[u8]) -> sha256::Hash {
+fn root_hash<'a, I: core::iter::Iterator<Item = TlvRecord<'a>>>(tlv_stream: I) -> sha256::Hash {
+       let mut tlv_stream = tlv_stream.peekable();
        let nonce_tag = tagged_hash_engine(sha256::Hash::from_engine({
-               let first_tlv_record = TlvStream::new(&data[..]).next().unwrap();
+               let first_tlv_record = tlv_stream.peek().unwrap();
                let mut engine = sha256::Hash::engine();
                engine.input("LnNonce".as_bytes());
                engine.input(first_tlv_record.record_bytes);
@@ -149,8 +164,7 @@ fn root_hash(data: &[u8]) -> sha256::Hash {
        let branch_tag = tagged_hash_engine(sha256::Hash::hash("LnBranch".as_bytes()));
 
        let mut leaves = Vec::new();
-       let tlv_stream = TlvStream::new(&data[..]);
-       for record in tlv_stream.skip_signatures() {
+       for record in TlvStream::skip_signatures(tlv_stream) {
                leaves.push(tagged_hash_from_engine(leaf_tag.clone(), &record.record_bytes));
                leaves.push(tagged_hash_from_engine(nonce_tag.clone(), &record.type_bytes));
        }
@@ -227,8 +241,10 @@ impl<'a> TlvStream<'a> {
                        .take_while(move |record| take_range.contains(&record.r#type))
        }
 
-       fn skip_signatures(self) -> core::iter::Filter<TlvStream<'a>, fn(&TlvRecord) -> bool> {
-               self.filter(|record| !SIGNATURE_TYPES.contains(&record.r#type))
+       fn skip_signatures(
+               tlv_stream: impl core::iter::Iterator<Item = TlvRecord<'a>>
+       ) -> impl core::iter::Iterator<Item = TlvRecord<'a>> {
+               tlv_stream.filter(|record| !SIGNATURE_TYPES.contains(&record.r#type))
        }
 }
 
@@ -276,7 +292,7 @@ impl<'a> Writeable for WithoutSignatures<'a> {
        #[inline]
        fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
                let tlv_stream = TlvStream::new(self.0);
-               for record in tlv_stream.skip_signatures() {
+               for record in TlvStream::skip_signatures(tlv_stream) {
                        writer.write_all(record.record_bytes)?;
                }
                Ok(())
@@ -289,7 +305,7 @@ mod tests {
 
        use bitcoin::hashes::{Hash, sha256};
        use bitcoin::hashes::hex::FromHex;
-       use bitcoin::secp256k1::{KeyPair, Message, Secp256k1, SecretKey};
+       use bitcoin::secp256k1::{Keypair, Message, Secp256k1, SecretKey};
        use bitcoin::secp256k1::schnorr::Signature;
        use crate::offers::offer::{Amount, OfferBuilder};
        use crate::offers::invoice_request::{InvoiceRequest, UnsignedInvoiceRequest};
@@ -304,15 +320,15 @@ mod tests {
                macro_rules! tlv2 { () => { "02080000010000020003" } }
                macro_rules! tlv3 { () => { "03310266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c0351800000000000000010000000000000002" } }
                assert_eq!(
-                       super::root_hash(&<Vec<u8>>::from_hex(tlv1!()).unwrap()),
+                       super::root_hash(TlvStream::new(&<Vec<u8>>::from_hex(tlv1!()).unwrap())),
                        sha256::Hash::from_slice(&<Vec<u8>>::from_hex("b013756c8fee86503a0b4abdab4cddeb1af5d344ca6fc2fa8b6c08938caa6f93").unwrap()).unwrap(),
                );
                assert_eq!(
-                       super::root_hash(&<Vec<u8>>::from_hex(concat!(tlv1!(), tlv2!())).unwrap()),
+                       super::root_hash(TlvStream::new(&<Vec<u8>>::from_hex(concat!(tlv1!(), tlv2!())).unwrap())),
                        sha256::Hash::from_slice(&<Vec<u8>>::from_hex("c3774abbf4815aa54ccaa026bff6581f01f3be5fe814c620a252534f434bc0d1").unwrap()).unwrap(),
                );
                assert_eq!(
-                       super::root_hash(&<Vec<u8>>::from_hex(concat!(tlv1!(), tlv2!(), tlv3!())).unwrap()),
+                       super::root_hash(TlvStream::new(&<Vec<u8>>::from_hex(concat!(tlv1!(), tlv2!(), tlv3!())).unwrap())),
                        sha256::Hash::from_slice(&<Vec<u8>>::from_hex("ab2e79b1283b0b31e0b035258de23782df6b89a38cfa7237bde69aed1a658c5d").unwrap()).unwrap(),
                );
        }
@@ -322,15 +338,16 @@ mod tests {
                let secp_ctx = Secp256k1::new();
                let recipient_pubkey = {
                        let secret_key = SecretKey::from_slice(&<Vec<u8>>::from_hex("4141414141414141414141414141414141414141414141414141414141414141").unwrap()).unwrap();
-                       KeyPair::from_secret_key(&secp_ctx, &secret_key).public_key()
+                       Keypair::from_secret_key(&secp_ctx, &secret_key).public_key()
                };
                let payer_keys = {
                        let secret_key = SecretKey::from_slice(&<Vec<u8>>::from_hex("4242424242424242424242424242424242424242424242424242424242424242").unwrap()).unwrap();
-                       KeyPair::from_secret_key(&secp_ctx, &secret_key)
+                       Keypair::from_secret_key(&secp_ctx, &secret_key)
                };
 
                // BOLT 12 test vectors
-               let invoice_request = OfferBuilder::new("A Mathematical Treatise".into(), recipient_pubkey)
+               let invoice_request = OfferBuilder::new(recipient_pubkey)
+                       .description("A Mathematical Treatise".into())
                        .amount(Amount::Currency { iso4217_code: *b"USD", amount: 100 })
                        .build_unchecked()
                        .request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
@@ -344,7 +361,7 @@ mod tests {
                        "lnr1qqyqqqqqqqqqqqqqqcp4256ypqqkgzshgysy6ct5dpjk6ct5d93kzmpq23ex2ct5d9ek293pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpjkppqvjx204vgdzgsqpvcp4mldl3plscny0rt707gvpdh6ndydfacz43euzqhrurageg3n7kafgsek6gz3e9w52parv8gs2hlxzk95tzeswywffxlkeyhml0hh46kndmwf4m6xma3tkq2lu04qz3slje2rfthc89vss",
                );
                assert_eq!(
-                       super::root_hash(&invoice_request.bytes[..]),
+                       super::root_hash(TlvStream::new(&invoice_request.bytes[..])),
                        sha256::Hash::from_slice(&<Vec<u8>>::from_hex("608407c18ad9a94d9ea2bcdbe170b6c20c462a7833a197621c916f78cf18e624").unwrap()).unwrap(),
                );
                assert_eq!(
@@ -355,7 +372,7 @@ mod tests {
 
         #[test]
         fn compute_tagged_hash() {
-                let unsigned_invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
+                let unsigned_invoice_request = OfferBuilder::new(recipient_pubkey())
                         .amount_msats(1000)
                         .build().unwrap()
                         .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
@@ -367,8 +384,7 @@ mod tests {
                 let tagged_hash = unsigned_invoice_request.as_ref();
                 let expected_digest = unsigned_invoice_request.as_ref().as_digest();
                 let tag = sha256::Hash::hash(tagged_hash.tag().as_bytes());
-                let actual_digest = Message::from_slice(super::tagged_hash(tag, tagged_hash.merkle_root()).as_byte_array())
-                        .unwrap();
+                let actual_digest = Message::from_digest(super::tagged_hash(tag, tagged_hash.merkle_root()).to_byte_array());
                 assert_eq!(*expected_digest, actual_digest);
         }
 
@@ -377,14 +393,14 @@ mod tests {
                let secp_ctx = Secp256k1::new();
                let recipient_pubkey = {
                        let secret_key = SecretKey::from_slice(&[41; 32]).unwrap();
-                       KeyPair::from_secret_key(&secp_ctx, &secret_key).public_key()
+                       Keypair::from_secret_key(&secp_ctx, &secret_key).public_key()
                };
                let payer_keys = {
                        let secret_key = SecretKey::from_slice(&[42; 32]).unwrap();
-                       KeyPair::from_secret_key(&secp_ctx, &secret_key)
+                       Keypair::from_secret_key(&secp_ctx, &secret_key)
                };
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey)
+               let invoice_request = OfferBuilder::new(recipient_pubkey)
                        .amount_msats(100)
                        .build_unchecked()
                        .request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
@@ -409,14 +425,14 @@ mod tests {
                let secp_ctx = Secp256k1::new();
                let recipient_pubkey = {
                        let secret_key = SecretKey::from_slice(&[41; 32]).unwrap();
-                       KeyPair::from_secret_key(&secp_ctx, &secret_key).public_key()
+                       Keypair::from_secret_key(&secp_ctx, &secret_key).public_key()
                };
                let payer_keys = {
                        let secret_key = SecretKey::from_slice(&[42; 32]).unwrap();
-                       KeyPair::from_secret_key(&secp_ctx, &secret_key)
+                       Keypair::from_secret_key(&secp_ctx, &secret_key)
                };
 
-               let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey)
+               let invoice_request = OfferBuilder::new(recipient_pubkey)
                        .amount_msats(100)
                        .build_unchecked()
                        .request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
index bf5e50deb77b8c290c1385f3459b76fb17bfbd6d..762bc1f3306555947f8a44aff9799547965753f5 100644 (file)
@@ -24,7 +24,7 @@
 //! use core::num::NonZeroU64;
 //! use core::time::Duration;
 //!
-//! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
+//! use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, SecretKey};
 //! use lightning::offers::offer::{Offer, OfferBuilder, Quantity};
 //! use lightning::offers::parse::Bolt12ParseError;
 //! use lightning::util::ser::{Readable, Writeable};
 //! # #[cfg(feature = "std")]
 //! # fn build() -> Result<(), Bolt12ParseError> {
 //! let secp_ctx = Secp256k1::new();
-//! let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+//! let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
 //! let pubkey = PublicKey::from(keys);
 //!
 //! let expiration = SystemTime::now() + Duration::from_secs(24 * 60 * 60);
-//! let offer = OfferBuilder::new("coffee, large".to_string(), pubkey)
+//! let offer = OfferBuilder::new(pubkey)
+//!     .description("coffee, large".to_string())
 //!     .amount_msats(20_000)
 //!     .supported_quantity(Quantity::Unbounded)
 //!     .absolute_expiry(expiration.duration_since(SystemTime::UNIX_EPOCH).unwrap())
@@ -77,9 +78,8 @@
 //! [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder
 
 use bitcoin::blockdata::constants::ChainHash;
-use bitcoin::network::constants::Network;
-use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, self};
-use core::convert::TryFrom;
+use bitcoin::network::Network;
+use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, self};
 use core::hash::{Hash, Hasher};
 use core::num::NonZeroU64;
 use core::ops::Deref;
@@ -91,11 +91,11 @@ use crate::blinded_path::BlindedPath;
 use crate::ln::channelmanager::PaymentId;
 use crate::ln::features::OfferFeatures;
 use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
-use crate::ln::msgs::MAX_VALUE_MSAT;
-use crate::offers::merkle::TlvStream;
+use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
+use crate::offers::merkle::{TaggedHash, TlvStream};
 use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
 use crate::offers::signer::{Metadata, MetadataMaterial, self};
-use crate::util::ser::{HighZeroBytesDroppedBigSize, WithoutLength, Writeable, Writer};
+use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
 use crate::util::string::PrintableString;
 
 #[cfg(not(c_bindings))]
@@ -107,6 +107,7 @@ use {
        crate::offers::invoice_request::{InvoiceRequestWithDerivedPayerIdBuilder, InvoiceRequestWithExplicitPayerIdBuilder},
 };
 
+#[allow(unused_imports)]
 use crate::prelude::*;
 
 #[cfg(feature = "std")]
@@ -114,6 +115,37 @@ use std::time::SystemTime;
 
 pub(super) const IV_BYTES: &[u8; IV_LEN] = b"LDK Offer ~~~~~~";
 
+/// An identifier for an [`Offer`] built using [`DerivedMetadata`].
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct OfferId(pub [u8; 32]);
+
+impl OfferId {
+       const ID_TAG: &'static str = "LDK Offer ID";
+
+       fn from_valid_offer_tlv_stream(bytes: &[u8]) -> Self {
+               let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(Self::ID_TAG, bytes);
+               Self(tagged_hash.to_bytes())
+       }
+
+       fn from_valid_invreq_tlv_stream(bytes: &[u8]) -> Self {
+               let tlv_stream = TlvStream::new(bytes).range(OFFER_TYPES);
+               let tagged_hash = TaggedHash::from_tlv_stream(Self::ID_TAG, tlv_stream);
+               Self(tagged_hash.to_bytes())
+       }
+}
+
+impl Writeable for OfferId {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               self.0.write(w)
+       }
+}
+
+impl Readable for OfferId {
+       fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
+               Ok(OfferId(Readable::read(r)?))
+       }
+}
+
 /// Builds an [`Offer`] for the "offer to be paid" flow.
 ///
 /// See [module-level documentation] for usage.
@@ -131,10 +163,9 @@ pub struct OfferBuilder<'a, M: MetadataStrategy, T: secp256k1::Signing> {
 ///
 /// 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
 #[cfg(c_bindings)]
+#[derive(Clone)]
 pub struct OfferWithExplicitMetadataBuilder<'a> {
        offer: OfferContents,
        metadata_strategy: core::marker::PhantomData<ExplicitMetadata>,
@@ -145,10 +176,9 @@ pub struct OfferWithExplicitMetadataBuilder<'a> {
 ///
 /// 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
 #[cfg(c_bindings)]
+#[derive(Clone)]
 pub struct OfferWithDerivedMetadataBuilder<'a> {
        offer: OfferContents,
        metadata_strategy: core::marker::PhantomData<DerivedMetadata>,
@@ -177,9 +207,8 @@ impl MetadataStrategy for DerivedMetadata {}
 macro_rules! offer_explicit_metadata_builder_methods { (
        $self: ident, $self_type: ty, $return_type: ty, $return_value: expr
 ) => {
-       /// Creates a new builder for an offer setting the [`Offer::description`] and using the
-       /// [`Offer::signing_pubkey`] for signing invoices. The associated secret key must be remembered
-       /// while the offer is valid.
+       /// Creates a new builder for an offer using the [`Offer::signing_pubkey`] for signing invoices.
+       /// The associated secret key must be remembered while the offer is valid.
        ///
        /// Use a different pubkey per offer to avoid correlating offers.
        ///
@@ -190,12 +219,12 @@ macro_rules! offer_explicit_metadata_builder_methods { (
        ///
        /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
        /// [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder
-       pub fn new(description: String, signing_pubkey: PublicKey) -> Self {
+       pub fn new(signing_pubkey: PublicKey) -> Self {
                Self {
                        offer: OfferContents {
-                               chains: None, metadata: None, amount: None, description,
+                               chains: None, metadata: None, amount: None, description: None,
                                features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
-                               supported_quantity: Quantity::One, signing_pubkey,
+                               supported_quantity: Quantity::One, signing_pubkey: Some(signing_pubkey),
                        },
                        metadata_strategy: core::marker::PhantomData,
                        secp_ctx: None,
@@ -224,7 +253,7 @@ macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => {
        /// [`InvoiceRequest::verify`]: crate::offers::invoice_request::InvoiceRequest::verify
        /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
        pub fn deriving_signing_pubkey<ES: Deref>(
-               description: String, node_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
+               node_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
                secp_ctx: &'a Secp256k1<$secp_context>
        ) -> Self where ES::Target: EntropySource {
                let nonce = Nonce::from_entropy_source(entropy_source);
@@ -232,9 +261,9 @@ macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => {
                let metadata = Metadata::DerivedSigningPubkey(derivation_material);
                Self {
                        offer: OfferContents {
-                               chains: None, metadata: Some(metadata), amount: None, description,
+                               chains: None, metadata: Some(metadata), amount: None, description: None,
                                features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
-                               supported_quantity: Quantity::One, signing_pubkey: node_id,
+                               supported_quantity: Quantity::One, signing_pubkey: Some(node_id),
                        },
                        metadata_strategy: core::marker::PhantomData,
                        secp_ctx: Some(secp_ctx),
@@ -294,6 +323,14 @@ macro_rules! offer_builder_methods { (
                $return_value
        }
 
+       /// Sets the [`Offer::description`].
+       ///
+       /// Successive calls to this method will override the previous setting.
+       pub fn description($($self_mut)* $self: $self_type, description: String) -> $return_type {
+               $self.offer.description = Some(description);
+               $return_value
+       }
+
        /// Sets the [`Offer::issuer`].
        ///
        /// Successive calls to this method will override the previous setting.
@@ -333,6 +370,10 @@ macro_rules! offer_builder_methods { (
                        None => {},
                }
 
+               if $self.offer.amount.is_some() && $self.offer.description.is_none() {
+                       $self.offer.description = Some(String::new());
+               }
+
                if let Some(chains) = &$self.offer.chains {
                        if chains.len() == 1 && chains[0] == $self.offer.implied_chain() {
                                $self.offer.chains = None;
@@ -360,7 +401,7 @@ macro_rules! offer_builder_methods { (
                                let (derived_metadata, keys) = metadata.derive_from(tlv_stream, $self.secp_ctx);
                                metadata = derived_metadata;
                                if let Some(keys) = keys {
-                                       $self.offer.signing_pubkey = keys.public_key();
+                                       $self.offer.signing_pubkey = Some(keys.public_key());
                                }
                        }
 
@@ -370,12 +411,15 @@ macro_rules! offer_builder_methods { (
                let mut bytes = Vec::new();
                $self.offer.write(&mut bytes).unwrap();
 
+               let id = OfferId::from_valid_offer_tlv_stream(&bytes);
+
                Offer {
                        bytes,
                        #[cfg(not(c_bindings))]
                        contents: $self.offer,
                        #[cfg(c_bindings)]
-                       contents: $self.offer.clone()
+                       contents: $self.offer.clone(),
+                       id,
                }
        }
 } }
@@ -402,6 +446,12 @@ macro_rules! offer_builder_test_methods { (
                $return_value
        }
 
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       pub(crate) fn clear_signing_pubkey($($self_mut)* $self: $self_type) -> $return_type {
+               $self.offer.signing_pubkey = None;
+               $return_value
+       }
+
        #[cfg_attr(c_bindings, allow(dead_code))]
        pub(super) fn build_unchecked($self: $self_type) -> Offer {
                $self.build_without_checks()
@@ -459,6 +509,16 @@ for OfferWithDerivedMetadataBuilder<'a> {
        }
 }
 
+#[cfg(c_bindings)]
+impl<'a> From<OfferWithDerivedMetadataBuilder<'a>>
+for OfferBuilder<'a, DerivedMetadata, secp256k1::All> {
+       fn from(builder: OfferWithDerivedMetadataBuilder<'a>) -> Self {
+               let OfferWithDerivedMetadataBuilder { offer, metadata_strategy, secp_ctx } = builder;
+
+               Self { offer, metadata_strategy, secp_ctx }
+       }
+}
+
 /// An `Offer` is a potentially long-lived proposal for payment of a good or service.
 ///
 /// An offer is a precursor to an [`InvoiceRequest`]. A merchant publishes an offer from which a
@@ -478,6 +538,7 @@ pub struct Offer {
        // fields.
        pub(super) bytes: Vec<u8>,
        pub(super) contents: OfferContents,
+       id: OfferId,
 }
 
 /// The contents of an [`Offer`], which may be shared with an [`InvoiceRequest`] or a
@@ -491,13 +552,13 @@ pub(super) struct OfferContents {
        chains: Option<Vec<ChainHash>>,
        metadata: Option<Metadata>,
        amount: Option<Amount>,
-       description: String,
+       description: Option<String>,
        features: OfferFeatures,
        absolute_expiry: Option<Duration>,
        issuer: Option<String>,
        paths: Option<Vec<BlindedPath>>,
        supported_quantity: Quantity,
-       signing_pubkey: PublicKey,
+       signing_pubkey: Option<PublicKey>,
 }
 
 macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
@@ -519,13 +580,13 @@ macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
        }
 
        /// The minimum amount required for a successful payment of a single item.
-       pub fn amount(&$self) -> Option<&$crate::offers::offer::Amount> {
+       pub fn amount(&$self) -> Option<$crate::offers::offer::Amount> {
                $contents.amount()
        }
 
        /// A complete description of the purpose of the payment. Intended to be displayed to the user
        /// but with the caveat that it has not been verified in any way.
-       pub fn description(&$self) -> $crate::util::string::PrintableString {
+       pub fn description(&$self) -> Option<$crate::util::string::PrintableString> {
                $contents.description()
        }
 
@@ -559,7 +620,7 @@ macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
        }
 
        /// The public key used by the recipient to sign invoices.
-       pub fn signing_pubkey(&$self) -> bitcoin::secp256k1::PublicKey {
+       pub fn signing_pubkey(&$self) -> Option<bitcoin::secp256k1::PublicKey> {
                $contents.signing_pubkey()
        }
 } }
@@ -567,6 +628,11 @@ macro_rules! offer_accessors { ($self: ident, $contents: expr) => {
 impl Offer {
        offer_accessors!(self, self.contents);
 
+       /// Returns the id of the offer.
+       pub fn id(&self) -> OfferId {
+               self.id
+       }
+
        pub(super) fn implied_chain(&self) -> ChainHash {
                self.contents.implied_chain()
        }
@@ -740,12 +806,12 @@ impl OfferContents {
                self.metadata.as_ref().and_then(|metadata| metadata.as_bytes())
        }
 
-       pub fn amount(&self) -> Option<&Amount> {
-               self.amount.as_ref()
+       pub fn amount(&self) -> Option<Amount> {
+               self.amount
        }
 
-       pub fn description(&self) -> PrintableString {
-               PrintableString(&self.description)
+       pub fn description(&self) -> Option<PrintableString> {
+               self.description.as_ref().map(|description| PrintableString(description))
        }
 
        pub fn features(&self) -> &OfferFeatures {
@@ -836,14 +902,14 @@ impl OfferContents {
                }
        }
 
-       pub(super) fn signing_pubkey(&self) -> PublicKey {
+       pub(super) fn signing_pubkey(&self) -> Option<PublicKey> {
                self.signing_pubkey
        }
 
        /// Verifies that the offer metadata was produced from the offer in the TLV stream.
        pub(super) fn verify<T: secp256k1::Signing>(
                &self, bytes: &[u8], key: &ExpandedKey, secp_ctx: &Secp256k1<T>
-       ) -> Result<Option<KeyPair>, ()> {
+       ) -> Result<(OfferId, Option<Keypair>), ()> {
                match self.metadata() {
                        Some(metadata) => {
                                let tlv_stream = TlvStream::new(bytes).range(OFFER_TYPES).filter(|record| {
@@ -855,9 +921,17 @@ impl OfferContents {
                                                _ => true,
                                        }
                                });
-                               signer::verify_recipient_metadata(
-                                       metadata, key, IV_BYTES, self.signing_pubkey(), tlv_stream, secp_ctx
-                               )
+                               let signing_pubkey = match self.signing_pubkey() {
+                                       Some(signing_pubkey) => signing_pubkey,
+                                       None => return Err(()),
+                               };
+                               let keys = signer::verify_recipient_metadata(
+                                       metadata, key, IV_BYTES, signing_pubkey, tlv_stream, secp_ctx
+                               )?;
+
+                               let offer_id = OfferId::from_valid_invreq_tlv_stream(bytes);
+
+                               Ok((offer_id, keys))
                        },
                        None => Err(()),
                }
@@ -881,13 +955,13 @@ impl OfferContents {
                        metadata: self.metadata(),
                        currency,
                        amount,
-                       description: Some(&self.description),
+                       description: self.description.as_ref(),
                        features,
                        absolute_expiry: self.absolute_expiry.map(|duration| duration.as_secs()),
                        paths: self.paths.as_ref(),
                        issuer: self.issuer.as_ref(),
                        quantity_max: self.supported_quantity.to_tlv_record(),
-                       node_id: Some(&self.signing_pubkey),
+                       node_id: self.signing_pubkey.as_ref(),
                }
        }
 }
@@ -906,7 +980,7 @@ impl Writeable for OfferContents {
 
 /// The minimum amount required for an item in an [`Offer`], denominated in either bitcoin or
 /// another currency.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq)]
 pub enum Amount {
        /// An amount of bitcoin.
        Bitcoin {
@@ -992,7 +1066,9 @@ impl TryFrom<Vec<u8>> for Offer {
                let offer = ParsedMessage::<OfferTlvStream>::try_from(bytes)?;
                let ParsedMessage { bytes, tlv_stream } = offer;
                let contents = OfferContents::try_from(tlv_stream)?;
-               Ok(Offer { bytes, contents })
+               let id = OfferId::from_valid_offer_tlv_stream(&bytes);
+
+               Ok(Offer { bytes, contents, id })
        }
 }
 
@@ -1017,10 +1093,9 @@ impl TryFrom<OfferTlvStream> for OfferContents {
                        (Some(iso4217_code), Some(amount)) => Some(Amount::Currency { iso4217_code, amount }),
                };
 
-               let description = match description {
-                       None => return Err(Bolt12SemanticError::MissingDescription),
-                       Some(description) => description,
-               };
+               if amount.is_some() && description.is_none() {
+                       return Err(Bolt12SemanticError::MissingDescription);
+               }
 
                let features = features.unwrap_or_else(OfferFeatures::empty);
 
@@ -1033,9 +1108,10 @@ impl TryFrom<OfferTlvStream> for OfferContents {
                        Some(n) => Quantity::Bounded(NonZeroU64::new(n).unwrap()),
                };
 
-               let signing_pubkey = match node_id {
-                       None => return Err(Bolt12SemanticError::MissingSigningPubkey),
-                       Some(node_id) => node_id,
+               let (signing_pubkey, paths) = match (node_id, paths) {
+                       (None, None) => return Err(Bolt12SemanticError::MissingSigningPubkey),
+                       (_, Some(paths)) if paths.is_empty() => return Err(Bolt12SemanticError::MissingPaths),
+                       (node_id, paths) => (node_id, paths),
                };
 
                Ok(OfferContents {
@@ -1064,12 +1140,11 @@ mod tests {
        };
 
        use bitcoin::blockdata::constants::ChainHash;
-       use bitcoin::network::constants::Network;
+       use bitcoin::network::Network;
        use bitcoin::secp256k1::Secp256k1;
-       use core::convert::TryFrom;
        use core::num::NonZeroU64;
        use core::time::Duration;
-       use crate::blinded_path::{BlindedHop, BlindedPath};
+       use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
        use crate::sign::KeyMaterial;
        use crate::ln::features::OfferFeatures;
        use crate::ln::inbound_payment::ExpandedKey;
@@ -1081,7 +1156,7 @@ mod tests {
 
        #[test]
        fn builds_offer_with_defaults() {
-               let offer = OfferBuilder::new("foo".into(), pubkey(42)).build().unwrap();
+               let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
 
                let mut buffer = Vec::new();
                offer.write(&mut buffer).unwrap();
@@ -1091,7 +1166,7 @@ mod tests {
                assert!(offer.supports_chain(ChainHash::using_genesis_block(Network::Bitcoin)));
                assert_eq!(offer.metadata(), None);
                assert_eq!(offer.amount(), None);
-               assert_eq!(offer.description(), PrintableString("foo"));
+               assert_eq!(offer.description(), None);
                assert_eq!(offer.offer_features(), &OfferFeatures::empty());
                assert_eq!(offer.absolute_expiry(), None);
                #[cfg(feature = "std")]
@@ -1099,7 +1174,8 @@ mod tests {
                assert_eq!(offer.paths(), &[]);
                assert_eq!(offer.issuer(), None);
                assert_eq!(offer.supported_quantity(), Quantity::One);
-               assert_eq!(offer.signing_pubkey(), pubkey(42));
+               assert!(!offer.expects_quantity());
+               assert_eq!(offer.signing_pubkey(), Some(pubkey(42)));
 
                assert_eq!(
                        offer.as_tlv_stream(),
@@ -1108,7 +1184,7 @@ mod tests {
                                metadata: None,
                                currency: None,
                                amount: None,
-                               description: Some(&String::from("foo")),
+                               description: None,
                                features: None,
                                absolute_expiry: None,
                                paths: None,
@@ -1128,7 +1204,7 @@ mod tests {
                let mainnet = ChainHash::using_genesis_block(Network::Bitcoin);
                let testnet = ChainHash::using_genesis_block(Network::Testnet);
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .chain(Network::Bitcoin)
                        .build()
                        .unwrap();
@@ -1136,7 +1212,7 @@ mod tests {
                assert_eq!(offer.chains(), vec![mainnet]);
                assert_eq!(offer.as_tlv_stream().chains, None);
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .chain(Network::Testnet)
                        .build()
                        .unwrap();
@@ -1144,7 +1220,7 @@ mod tests {
                assert_eq!(offer.chains(), vec![testnet]);
                assert_eq!(offer.as_tlv_stream().chains, Some(&vec![testnet]));
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .chain(Network::Testnet)
                        .chain(Network::Testnet)
                        .build()
@@ -1153,7 +1229,7 @@ mod tests {
                assert_eq!(offer.chains(), vec![testnet]);
                assert_eq!(offer.as_tlv_stream().chains, Some(&vec![testnet]));
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .chain(Network::Bitcoin)
                        .chain(Network::Testnet)
                        .build()
@@ -1166,14 +1242,14 @@ mod tests {
 
        #[test]
        fn builds_offer_with_metadata() {
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .metadata(vec![42; 32]).unwrap()
                        .build()
                        .unwrap();
                assert_eq!(offer.metadata(), Some(&vec![42; 32]));
                assert_eq!(offer.as_tlv_stream().metadata, Some(&vec![42; 32]));
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .metadata(vec![42; 32]).unwrap()
                        .metadata(vec![43; 32]).unwrap()
                        .build()
@@ -1184,7 +1260,6 @@ mod tests {
 
        #[test]
        fn builds_offer_with_metadata_derived() {
-               let desc = "foo".to_string();
                let node_id = recipient_pubkey();
                let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
                let entropy = FixedEntropy {};
@@ -1193,15 +1268,18 @@ mod tests {
                #[cfg(c_bindings)]
                use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
                let offer = OfferBuilder
-                       ::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
+                       ::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
                        .amount_msats(1000)
                        .build().unwrap();
-               assert_eq!(offer.signing_pubkey(), node_id);
+               assert_eq!(offer.signing_pubkey(), Some(node_id));
 
                let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
                        .build().unwrap()
                        .sign(payer_sign).unwrap();
-               assert!(invoice_request.verify(&expanded_key, &secp_ctx).is_ok());
+               match invoice_request.verify(&expanded_key, &secp_ctx) {
+                       Ok(invoice_request) => assert_eq!(invoice_request.offer_id, offer.id()),
+                       Err(_) => panic!("unexpected error"),
+               }
 
                // Fails verification with altered offer field
                let mut tlv_stream = offer.as_tlv_stream();
@@ -1233,14 +1311,13 @@ mod tests {
 
        #[test]
        fn builds_offer_with_derived_signing_pubkey() {
-               let desc = "foo".to_string();
                let node_id = recipient_pubkey();
                let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
                let entropy = FixedEntropy {};
                let secp_ctx = Secp256k1::new();
 
                let blinded_path = BlindedPath {
-                       introduction_node_id: pubkey(40),
+                       introduction_node: IntroductionNode::NodeId(pubkey(40)),
                        blinding_point: pubkey(41),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] },
@@ -1251,16 +1328,19 @@ mod tests {
                #[cfg(c_bindings)]
                use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
                let offer = OfferBuilder
-                       ::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
+                       ::deriving_signing_pubkey(node_id, &expanded_key, &entropy, &secp_ctx)
                        .amount_msats(1000)
                        .path(blinded_path)
                        .build().unwrap();
-               assert_ne!(offer.signing_pubkey(), node_id);
+               assert_ne!(offer.signing_pubkey(), Some(node_id));
 
                let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
                        .build().unwrap()
                        .sign(payer_sign).unwrap();
-               assert!(invoice_request.verify(&expanded_key, &secp_ctx).is_ok());
+               match invoice_request.verify(&expanded_key, &secp_ctx) {
+                       Ok(invoice_request) => assert_eq!(invoice_request.offer_id, offer.id()),
+                       Err(_) => panic!("unexpected error"),
+               }
 
                // Fails verification with altered offer field
                let mut tlv_stream = offer.as_tlv_stream();
@@ -1295,20 +1375,20 @@ mod tests {
                let bitcoin_amount = Amount::Bitcoin { amount_msats: 1000 };
                let currency_amount = Amount::Currency { iso4217_code: *b"USD", amount: 10 };
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .amount_msats(1000)
                        .build()
                        .unwrap();
                let tlv_stream = offer.as_tlv_stream();
-               assert_eq!(offer.amount(), Some(&bitcoin_amount));
+               assert_eq!(offer.amount(), Some(bitcoin_amount));
                assert_eq!(tlv_stream.amount, Some(1000));
                assert_eq!(tlv_stream.currency, None);
 
                #[cfg(not(c_bindings))]
-               let builder = OfferBuilder::new("foo".into(), pubkey(42))
+               let builder = OfferBuilder::new(pubkey(42))
                        .amount(currency_amount.clone());
                #[cfg(c_bindings)]
-               let mut builder = OfferBuilder::new("foo".into(), pubkey(42));
+               let mut builder = OfferBuilder::new(pubkey(42));
                #[cfg(c_bindings)]
                builder.amount(currency_amount.clone());
                let tlv_stream = builder.offer.as_tlv_stream();
@@ -1320,7 +1400,7 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12SemanticError::UnsupportedCurrency),
                }
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .amount(currency_amount.clone())
                        .amount(bitcoin_amount.clone())
                        .build()
@@ -1330,22 +1410,47 @@ mod tests {
                assert_eq!(tlv_stream.currency, None);
 
                let invalid_amount = Amount::Bitcoin { amount_msats: MAX_VALUE_MSAT + 1 };
-               match OfferBuilder::new("foo".into(), pubkey(42)).amount(invalid_amount).build() {
+               match OfferBuilder::new(pubkey(42)).amount(invalid_amount).build() {
                        Ok(_) => panic!("expected error"),
                        Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidAmount),
                }
        }
 
+       #[test]
+       fn builds_offer_with_description() {
+               let offer = OfferBuilder::new(pubkey(42))
+                       .description("foo".into())
+                       .build()
+                       .unwrap();
+               assert_eq!(offer.description(), Some(PrintableString("foo")));
+               assert_eq!(offer.as_tlv_stream().description, Some(&String::from("foo")));
+
+               let offer = OfferBuilder::new(pubkey(42))
+                       .description("foo".into())
+                       .description("bar".into())
+                       .build()
+                       .unwrap();
+               assert_eq!(offer.description(), Some(PrintableString("bar")));
+               assert_eq!(offer.as_tlv_stream().description, Some(&String::from("bar")));
+
+               let offer = OfferBuilder::new(pubkey(42))
+                       .amount_msats(1000)
+                       .build()
+                       .unwrap();
+               assert_eq!(offer.description(), Some(PrintableString("")));
+               assert_eq!(offer.as_tlv_stream().description, Some(&String::from("")));
+       }
+
        #[test]
        fn builds_offer_with_features() {
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .features_unchecked(OfferFeatures::unknown())
                        .build()
                        .unwrap();
                assert_eq!(offer.offer_features(), &OfferFeatures::unknown());
                assert_eq!(offer.as_tlv_stream().features, Some(&OfferFeatures::unknown()));
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .features_unchecked(OfferFeatures::unknown())
                        .features_unchecked(OfferFeatures::empty())
                        .build()
@@ -1360,7 +1465,7 @@ mod tests {
                let past_expiry = Duration::from_secs(0);
                let now = future_expiry - Duration::from_secs(1_000);
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .absolute_expiry(future_expiry)
                        .build()
                        .unwrap();
@@ -1370,7 +1475,7 @@ mod tests {
                assert_eq!(offer.absolute_expiry(), Some(future_expiry));
                assert_eq!(offer.as_tlv_stream().absolute_expiry, Some(future_expiry.as_secs()));
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .absolute_expiry(future_expiry)
                        .absolute_expiry(past_expiry)
                        .build()
@@ -1386,7 +1491,7 @@ mod tests {
        fn builds_offer_with_paths() {
                let paths = vec![
                        BlindedPath {
-                               introduction_node_id: pubkey(40),
+                               introduction_node: IntroductionNode::NodeId(pubkey(40)),
                                blinding_point: pubkey(41),
                                blinded_hops: vec![
                                        BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
@@ -1394,7 +1499,7 @@ mod tests {
                                ],
                        },
                        BlindedPath {
-                               introduction_node_id: pubkey(40),
+                               introduction_node: IntroductionNode::NodeId(pubkey(40)),
                                blinding_point: pubkey(41),
                                blinded_hops: vec![
                                        BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
@@ -1403,14 +1508,14 @@ mod tests {
                        },
                ];
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .path(paths[0].clone())
                        .path(paths[1].clone())
                        .build()
                        .unwrap();
                let tlv_stream = offer.as_tlv_stream();
                assert_eq!(offer.paths(), paths.as_slice());
-               assert_eq!(offer.signing_pubkey(), pubkey(42));
+               assert_eq!(offer.signing_pubkey(), Some(pubkey(42)));
                assert_ne!(pubkey(42), pubkey(44));
                assert_eq!(tlv_stream.paths, Some(&paths));
                assert_eq!(tlv_stream.node_id, Some(&pubkey(42)));
@@ -1418,20 +1523,20 @@ mod tests {
 
        #[test]
        fn builds_offer_with_issuer() {
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
-                       .issuer("bar".into())
+               let offer = OfferBuilder::new(pubkey(42))
+                       .issuer("foo".into())
                        .build()
                        .unwrap();
-               assert_eq!(offer.issuer(), Some(PrintableString("bar")));
-               assert_eq!(offer.as_tlv_stream().issuer, Some(&String::from("bar")));
+               assert_eq!(offer.issuer(), Some(PrintableString("foo")));
+               assert_eq!(offer.as_tlv_stream().issuer, Some(&String::from("foo")));
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
+                       .issuer("foo".into())
                        .issuer("bar".into())
-                       .issuer("baz".into())
                        .build()
                        .unwrap();
-               assert_eq!(offer.issuer(), Some(PrintableString("baz")));
-               assert_eq!(offer.as_tlv_stream().issuer, Some(&String::from("baz")));
+               assert_eq!(offer.issuer(), Some(PrintableString("bar")));
+               assert_eq!(offer.as_tlv_stream().issuer, Some(&String::from("bar")));
        }
 
        #[test]
@@ -1439,51 +1544,56 @@ mod tests {
                let one = NonZeroU64::new(1).unwrap();
                let ten = NonZeroU64::new(10).unwrap();
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::One)
                        .build()
                        .unwrap();
                let tlv_stream = offer.as_tlv_stream();
+               assert!(!offer.expects_quantity());
                assert_eq!(offer.supported_quantity(), Quantity::One);
                assert_eq!(tlv_stream.quantity_max, None);
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::Unbounded)
                        .build()
                        .unwrap();
                let tlv_stream = offer.as_tlv_stream();
+               assert!(offer.expects_quantity());
                assert_eq!(offer.supported_quantity(), Quantity::Unbounded);
                assert_eq!(tlv_stream.quantity_max, Some(0));
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::Bounded(ten))
                        .build()
                        .unwrap();
                let tlv_stream = offer.as_tlv_stream();
+               assert!(offer.expects_quantity());
                assert_eq!(offer.supported_quantity(), Quantity::Bounded(ten));
                assert_eq!(tlv_stream.quantity_max, Some(10));
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::Bounded(one))
                        .build()
                        .unwrap();
                let tlv_stream = offer.as_tlv_stream();
+               assert!(offer.expects_quantity());
                assert_eq!(offer.supported_quantity(), Quantity::Bounded(one));
                assert_eq!(tlv_stream.quantity_max, Some(1));
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::Bounded(ten))
                        .supported_quantity(Quantity::One)
                        .build()
                        .unwrap();
                let tlv_stream = offer.as_tlv_stream();
+               assert!(!offer.expects_quantity());
                assert_eq!(offer.supported_quantity(), Quantity::One);
                assert_eq!(tlv_stream.quantity_max, None);
        }
 
        #[test]
        fn fails_requesting_invoice_with_unknown_required_features() {
-               match OfferBuilder::new("foo".into(), pubkey(42))
+               match OfferBuilder::new(pubkey(42))
                        .features_unchecked(OfferFeatures::unknown())
                        .build().unwrap()
                        .request_invoice(vec![1; 32], pubkey(43))
@@ -1495,7 +1605,7 @@ mod tests {
 
        #[test]
        fn parses_offer_with_chains() {
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .chain(Network::Bitcoin)
                        .chain(Network::Testnet)
                        .build()
@@ -1507,7 +1617,7 @@ mod tests {
 
        #[test]
        fn parses_offer_with_amount() {
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .amount(Amount::Bitcoin { amount_msats: 1000 })
                        .build()
                        .unwrap();
@@ -1553,7 +1663,15 @@ mod tests {
 
        #[test]
        fn parses_offer_with_description() {
-               let offer = OfferBuilder::new("foo".into(), pubkey(42)).build().unwrap();
+               let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
+               if let Err(e) = offer.to_string().parse::<Offer>() {
+                       panic!("error parsing offer: {:?}", e);
+               }
+
+               let offer = OfferBuilder::new(pubkey(42))
+                       .description("foo".to_string())
+                       .amount_msats(1000)
+                       .build().unwrap();
                if let Err(e) = offer.to_string().parse::<Offer>() {
                        panic!("error parsing offer: {:?}", e);
                }
@@ -1574,9 +1692,9 @@ mod tests {
 
        #[test]
        fn parses_offer_with_paths() {
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .path(BlindedPath {
-                               introduction_node_id: pubkey(40),
+                               introduction_node: IntroductionNode::NodeId(pubkey(40)),
                                blinding_point: pubkey(41),
                                blinded_hops: vec![
                                        BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
@@ -1584,7 +1702,7 @@ mod tests {
                                ],
                        })
                        .path(BlindedPath {
-                               introduction_node_id: pubkey(40),
+                               introduction_node: IntroductionNode::NodeId(pubkey(40)),
                                blinding_point: pubkey(41),
                                blinded_hops: vec![
                                        BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
@@ -1597,18 +1715,37 @@ mod tests {
                        panic!("error parsing offer: {:?}", e);
                }
 
-               let mut builder = OfferBuilder::new("foo".into(), pubkey(42));
+               let offer = OfferBuilder::new(pubkey(42))
+                       .path(BlindedPath {
+                               introduction_node: IntroductionNode::NodeId(pubkey(40)),
+                               blinding_point: pubkey(41),
+                               blinded_hops: vec![
+                                       BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
+                                       BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
+                               ],
+                       })
+                       .clear_signing_pubkey()
+                       .build()
+                       .unwrap();
+               if let Err(e) = offer.to_string().parse::<Offer>() {
+                       panic!("error parsing offer: {:?}", e);
+               }
+
+               let mut builder = OfferBuilder::new(pubkey(42));
                builder.offer.paths = Some(vec![]);
 
                let offer = builder.build().unwrap();
-               if let Err(e) = offer.to_string().parse::<Offer>() {
-                       panic!("error parsing offer: {:?}", e);
+               match offer.to_string().parse::<Offer>() {
+                       Ok(_) => panic!("expected error"),
+                       Err(e) => {
+                               assert_eq!(e, Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingPaths));
+                       },
                }
        }
 
        #[test]
        fn parses_offer_with_quantity() {
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::One)
                        .build()
                        .unwrap();
@@ -1616,7 +1753,7 @@ mod tests {
                        panic!("error parsing offer: {:?}", e);
                }
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::Unbounded)
                        .build()
                        .unwrap();
@@ -1624,7 +1761,7 @@ mod tests {
                        panic!("error parsing offer: {:?}", e);
                }
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::Bounded(NonZeroU64::new(10).unwrap()))
                        .build()
                        .unwrap();
@@ -1632,7 +1769,7 @@ mod tests {
                        panic!("error parsing offer: {:?}", e);
                }
 
-               let offer = OfferBuilder::new("foo".into(), pubkey(42))
+               let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::Bounded(NonZeroU64::new(1).unwrap()))
                        .build()
                        .unwrap();
@@ -1643,7 +1780,7 @@ mod tests {
 
        #[test]
        fn parses_offer_with_node_id() {
-               let offer = OfferBuilder::new("foo".into(), pubkey(42)).build().unwrap();
+               let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
                if let Err(e) = offer.to_string().parse::<Offer>() {
                        panic!("error parsing offer: {:?}", e);
                }
@@ -1664,7 +1801,7 @@ mod tests {
 
        #[test]
        fn fails_parsing_offer_with_extra_tlv_records() {
-               let offer = OfferBuilder::new("foo".into(), pubkey(42)).build().unwrap();
+               let offer = OfferBuilder::new(pubkey(42)).build().unwrap();
 
                let mut encoded_offer = Vec::new();
                offer.write(&mut encoded_offer).unwrap();
@@ -1729,6 +1866,9 @@ mod bolt12_tests {
                        // with blinded path via Bob (0x424242...), blinding 020202...
                        "lno1pgx9getnwss8vetrw3hhyucs5ypjgef743p5fzqq9nqxh0ah7y87rzv3ud0eleps9kl2d5348hq2k8qzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgqpqqqqqqqqqqqqqqqqqqqqqqqqqqqzqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqzq3zyg3zyg3zyg3vggzamrjghtt05kvkvpcp0a79gmy3nt6jsn98ad2xs8de6sl9qmgvcvs",
 
+                       // ... and with sciddir introduction node
+                       "lno1pgx9getnwss8vetrw3hhyucs3yqqqqqqqqqqqqp2qgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqqyqqqqqqqqqqqqqqqqqqqqqqqqqqqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqqgzyg3zyg3zyg3z93pqthvwfzadd7jejes8q9lhc4rvjxd022zv5l44g6qah82ru5rdpnpj",
+
                        // ... and with second blinded path via Carol (0x434343...), blinding 020202...
                        "lno1pgx9getnwss8vetrw3hhyucsl5q5yqeyv5l2cs6y3qqzesrth7mlzrlp3xg7xhulusczm04x6g6nms9trspqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqqsqqqqqqqqqqqqqqqqqqqqqqqqqqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqpqg3zyg3zyg3zygz0uc7h32x9s0aecdhxlk075kn046aafpuuyw8f5j652t3vha2yqrsyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsqzqqqqqqqqqqqqqqqqqqqqqqqqqqqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqqyzyg3zyg3zyg3zzcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese",
 
@@ -1759,7 +1899,7 @@ mod bolt12_tests {
                // Malformed: empty
                assert_eq!(
                        "lno1".parse::<Offer>(),
-                       Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription)),
+                       Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSigningPubkey)),
                );
 
                // Malformed: truncated at type
@@ -1884,7 +2024,8 @@ mod bolt12_tests {
 
                // Missing offer_description
                assert_eq!(
-                       "lno1zcss9mk8y3wkklfvevcrszlmu23kfrxh49px20665dqwmn4p72pksese".parse::<Offer>(),
+                       // TODO: Match the spec once it is updated.
+                       "lno1pqpq86qkyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg".parse::<Offer>(),
                        Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingDescription)),
                );
 
index 400cf51a2944d47cc0afd19e227acb3216242959..472e44f6220f1aed6b2c77c71252a49a1b7aff3b 100644 (file)
@@ -9,13 +9,12 @@
 
 //! Parsing and formatting for bech32 message encoding.
 
-use bitcoin::bech32;
 use bitcoin::secp256k1;
-use core::convert::TryFrom;
 use crate::io;
 use crate::ln::msgs::DecodeError;
 use crate::util::ser::SeekReadable;
 
+#[allow(unused_imports)]
 use crate::prelude::*;
 
 #[cfg(not(fuzzing))]
@@ -25,12 +24,11 @@ pub(super) use sealed::Bech32Encode;
 pub use sealed::Bech32Encode;
 
 mod sealed {
-       use bitcoin::bech32;
-       use bitcoin::bech32::{FromBase32, ToBase32};
-       use core::convert::TryFrom;
+       use bech32::{FromBase32, ToBase32};
        use core::fmt;
        use super::Bolt12ParseError;
 
+       #[allow(unused_imports)]
        use crate::prelude::*;
 
        /// Indicates a message can be encoded using bech32.
@@ -183,6 +181,8 @@ pub enum Bolt12SemanticError {
        DuplicatePaymentId,
        /// Blinded paths were expected but were missing.
        MissingPaths,
+       /// Blinded paths were provided but were not expected.
+       UnexpectedPaths,
        /// The blinded payinfo given does not match the number of blinded path hops.
        InvalidPayInfo,
        /// An invoice creation time was expected but was missing.
@@ -275,7 +275,6 @@ mod bolt12_tests {
 #[cfg(test)]
 mod tests {
        use super::Bolt12ParseError;
-       use bitcoin::bech32;
        use crate::ln::msgs::DecodeError;
        use crate::offers::offer::Offer;
 
index 19aef23363d8e0afc9a5ddd758d5808098d1d03f..22180ce5f66fef52a2cba2a434c61e769cb287b8 100644 (file)
@@ -12,6 +12,7 @@
 use crate::offers::signer::Metadata;
 use crate::util::ser::WithoutLength;
 
+#[allow(unused_imports)]
 use crate::prelude::*;
 
 /// An unpredictable sequence of bytes typically containing information needed to derive
index 42177960868ab553ee1385f2569146dc8209e2bf..d796971f8b366f721174895a643b6f6d2cb67ecc 100644 (file)
@@ -28,8 +28,8 @@
 //! use core::convert::TryFrom;
 //! use core::time::Duration;
 //!
-//! use bitcoin::network::constants::Network;
-//! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
+//! use bitcoin::network::Network;
+//! use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, SecretKey};
 //! use lightning::offers::parse::Bolt12ParseError;
 //! use lightning::offers::refund::{Refund, RefundBuilder};
 //! use lightning::util::ser::{Readable, Writeable};
 //! # #[cfg(feature = "std")]
 //! # fn build() -> Result<(), Bolt12ParseError> {
 //! let secp_ctx = Secp256k1::new();
-//! let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+//! let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
 //! let pubkey = PublicKey::from(keys);
 //!
 //! let expiration = SystemTime::now() + Duration::from_secs(24 * 60 * 60);
-//! let refund = RefundBuilder::new("coffee, large".to_string(), vec![1; 32], pubkey, 20_000)?
+//! let refund = RefundBuilder::new(vec![1; 32], pubkey, 20_000)?
+//!     .description("coffee, large".to_string())
 //!     .absolute_expiry(expiration.duration_since(SystemTime::UNIX_EPOCH).unwrap())
 //!     .issuer("Foo Bar".to_string())
 //!     .path(create_blinded_path())
@@ -82,9 +83,8 @@
 //! [`ChannelManager::create_refund_builder`]: crate::ln::channelmanager::ChannelManager::create_refund_builder
 
 use bitcoin::blockdata::constants::ChainHash;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 use bitcoin::secp256k1::{PublicKey, Secp256k1, self};
-use core::convert::TryFrom;
 use core::hash::{Hash, Hasher};
 use core::ops::Deref;
 use core::str::FromStr;
@@ -92,7 +92,7 @@ use core::time::Duration;
 use crate::sign::EntropySource;
 use crate::io;
 use crate::blinded_path::BlindedPath;
-use crate::ln::PaymentHash;
+use crate::ln::types::PaymentHash;
 use crate::ln::channelmanager::PaymentId;
 use crate::ln::features::InvoiceRequestFeatures;
 use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
@@ -115,6 +115,7 @@ use {
        crate::offers::invoice::{InvoiceWithDerivedSigningPubkeyBuilder, InvoiceWithExplicitSigningPubkeyBuilder},
 };
 
+#[allow(unused_imports)]
 use crate::prelude::*;
 
 #[cfg(feature = "std")]
@@ -140,6 +141,7 @@ pub struct RefundBuilder<'a, T: secp256k1::Signing> {
 ///
 /// [module-level documentation]: self
 #[cfg(c_bindings)]
+#[derive(Clone)]
 pub struct RefundMaybeWithDerivedMetadataBuilder<'a> {
        refund: RefundContents,
        secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
@@ -149,8 +151,8 @@ macro_rules! refund_explicit_metadata_builder_methods { () => {
        /// Creates a new builder for a refund using the [`Refund::payer_id`] for the public node id to
        /// send to if no [`Refund::paths`] are set. Otherwise, it may be a transient pubkey.
        ///
-       /// Additionally, sets the required [`Refund::description`], [`Refund::payer_metadata`], and
-       /// [`Refund::amount_msats`].
+       /// Additionally, sets the required (empty) [`Refund::description`], [`Refund::payer_metadata`],
+       /// and [`Refund::amount_msats`].
        ///
        /// # Note
        ///
@@ -160,7 +162,7 @@ macro_rules! refund_explicit_metadata_builder_methods { () => {
        /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
        /// [`ChannelManager::create_refund_builder`]: crate::ln::channelmanager::ChannelManager::create_refund_builder
        pub fn new(
-               description: String, metadata: Vec<u8>, payer_id: PublicKey, amount_msats: u64
+               metadata: Vec<u8>, payer_id: PublicKey, amount_msats: u64
        ) -> Result<Self, Bolt12SemanticError> {
                if amount_msats > MAX_VALUE_MSAT {
                        return Err(Bolt12SemanticError::InvalidAmount);
@@ -169,9 +171,9 @@ macro_rules! refund_explicit_metadata_builder_methods { () => {
                let metadata = Metadata::Bytes(metadata);
                Ok(Self {
                        refund: RefundContents {
-                               payer: PayerContents(metadata), description, absolute_expiry: None, issuer: None,
-                               paths: None, chain: None, amount_msats, features: InvoiceRequestFeatures::empty(),
-                               quantity: None, payer_id, payer_note: None,
+                               payer: PayerContents(metadata), description: String::new(), absolute_expiry: None,
+                               issuer: None, chain: None, amount_msats, features: InvoiceRequestFeatures::empty(),
+                               quantity: None, payer_id, payer_note: None, paths: None,
                        },
                        secp_ctx: None,
                })
@@ -195,7 +197,7 @@ macro_rules! refund_builder_methods { (
        /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
        /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
        pub fn deriving_payer_id<ES: Deref>(
-               description: String, node_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
+               node_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
                secp_ctx: &'a Secp256k1<$secp_context>, amount_msats: u64, payment_id: PaymentId
        ) -> Result<Self, Bolt12SemanticError> where ES::Target: EntropySource {
                if amount_msats > MAX_VALUE_MSAT {
@@ -208,14 +210,22 @@ macro_rules! refund_builder_methods { (
                let metadata = Metadata::DerivedSigningPubkey(derivation_material);
                Ok(Self {
                        refund: RefundContents {
-                               payer: PayerContents(metadata), description, absolute_expiry: None, issuer: None,
-                               paths: None, chain: None, amount_msats, features: InvoiceRequestFeatures::empty(),
-                               quantity: None, payer_id: node_id, payer_note: None,
+                               payer: PayerContents(metadata), description: String::new(), absolute_expiry: None,
+                               issuer: None, chain: None, amount_msats, features: InvoiceRequestFeatures::empty(),
+                               quantity: None, payer_id: node_id, payer_note: None, paths: None,
                        },
                        secp_ctx: Some(secp_ctx),
                })
        }
 
+       /// Sets the [`Refund::description`].
+       ///
+       /// Successive calls to this method will override the previous setting.
+       pub fn description($($self_mut)* $self: $self_type, description: String) -> $return_type {
+               $self.refund.description = description;
+               $return_value
+       }
+
        /// Sets the [`Refund::absolute_expiry`] as seconds since the Unix epoch. Any expiry that has
        /// already passed is valid and can be checked for using [`Refund::is_expired`].
        ///
@@ -375,6 +385,16 @@ for RefundMaybeWithDerivedMetadataBuilder<'a> {
        }
 }
 
+#[cfg(c_bindings)]
+impl<'a> From<RefundMaybeWithDerivedMetadataBuilder<'a>>
+for RefundBuilder<'a, secp256k1::All> {
+       fn from(builder: RefundMaybeWithDerivedMetadataBuilder<'a>) -> Self {
+               let RefundMaybeWithDerivedMetadataBuilder { refund, secp_ctx } = builder;
+
+               Self { refund, secp_ctx }
+       }
+}
+
 /// A `Refund` is a request to send an [`Bolt12Invoice`] without a preceding [`Offer`].
 ///
 /// Typically, after an invoice is paid, the recipient may publish a refund allowing the sender to
@@ -400,7 +420,6 @@ pub(super) struct RefundContents {
        description: String,
        absolute_expiry: Option<Duration>,
        issuer: Option<String>,
-       paths: Option<Vec<BlindedPath>>,
        // invoice_request fields
        chain: Option<ChainHash>,
        amount_msats: u64,
@@ -408,6 +427,7 @@ pub(super) struct RefundContents {
        quantity: Option<u64>,
        payer_id: PublicKey,
        payer_note: Option<String>,
+       paths: Option<Vec<BlindedPath>>,
 }
 
 impl Refund {
@@ -724,7 +744,7 @@ impl RefundContents {
                        description: Some(&self.description),
                        features: None,
                        absolute_expiry: self.absolute_expiry.map(|duration| duration.as_secs()),
-                       paths: self.paths.as_ref(),
+                       paths: None,
                        issuer: self.issuer.as_ref(),
                        quantity_max: None,
                        node_id: None,
@@ -742,6 +762,7 @@ impl RefundContents {
                        quantity: self.quantity,
                        payer_id: Some(&self.payer_id),
                        payer_note: self.payer_note.as_ref(),
+                       paths: self.paths.as_ref(),
                };
 
                (payer, offer, invoice_request)
@@ -810,9 +831,12 @@ impl TryFrom<RefundTlvStream> for RefundContents {
                        PayerTlvStream { metadata: payer_metadata },
                        OfferTlvStream {
                                chains, metadata, currency, amount: offer_amount, description,
-                               features: offer_features, absolute_expiry, paths, issuer, quantity_max, node_id,
+                               features: offer_features, absolute_expiry, paths: offer_paths, issuer, quantity_max,
+                               node_id,
+                       },
+                       InvoiceRequestTlvStream {
+                               chain, amount, features, quantity, payer_id, payer_note, paths
                        },
-                       InvoiceRequestTlvStream { chain, amount, features, quantity, payer_id, payer_note },
                ) = tlv_stream;
 
                let payer = match payer_metadata {
@@ -843,6 +867,10 @@ impl TryFrom<RefundTlvStream> for RefundContents {
 
                let absolute_expiry = absolute_expiry.map(Duration::from_secs);
 
+               if offer_paths.is_some() {
+                       return Err(Bolt12SemanticError::UnexpectedPaths);
+               }
+
                if quantity_max.is_some() {
                        return Err(Bolt12SemanticError::UnexpectedQuantity);
                }
@@ -867,8 +895,8 @@ impl TryFrom<RefundTlvStream> for RefundContents {
                };
 
                Ok(RefundContents {
-                       payer, description, absolute_expiry, issuer, paths, chain, amount_msats, features,
-                       quantity, payer_id, payer_note,
+                       payer, description, absolute_expiry, issuer, chain, amount_msats, features, quantity,
+                       payer_id, payer_note, paths,
                })
        }
 }
@@ -892,11 +920,12 @@ mod tests {
        };
 
        use bitcoin::blockdata::constants::ChainHash;
-       use bitcoin::network::constants::Network;
-       use bitcoin::secp256k1::{KeyPair, Secp256k1, SecretKey};
-       use core::convert::TryFrom;
+       use bitcoin::network::Network;
+       use bitcoin::secp256k1::{Keypair, Secp256k1, SecretKey};
+
        use core::time::Duration;
-       use crate::blinded_path::{BlindedHop, BlindedPath};
+
+       use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
        use crate::sign::KeyMaterial;
        use crate::ln::channelmanager::PaymentId;
        use crate::ln::features::{InvoiceRequestFeatures, OfferFeatures};
@@ -909,6 +938,7 @@ mod tests {
        use crate::offers::test_utils::*;
        use crate::util::ser::{BigSize, Writeable};
        use crate::util::string::PrintableString;
+       use crate::prelude::*;
 
        trait ToBytes {
                fn to_bytes(&self) -> Vec<u8>;
@@ -924,7 +954,7 @@ mod tests {
 
        #[test]
        fn builds_refund_with_defaults() {
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .build().unwrap();
 
                let mut buffer = Vec::new();
@@ -932,7 +962,7 @@ mod tests {
 
                assert_eq!(refund.bytes, buffer.as_slice());
                assert_eq!(refund.payer_metadata(), &[1; 32]);
-               assert_eq!(refund.description(), PrintableString("foo"));
+               assert_eq!(refund.description(), PrintableString(""));
                assert_eq!(refund.absolute_expiry(), None);
                #[cfg(feature = "std")]
                assert!(!refund.is_expired());
@@ -953,7 +983,7 @@ mod tests {
                                        metadata: None,
                                        currency: None,
                                        amount: None,
-                                       description: Some(&String::from("foo")),
+                                       description: Some(&String::from("")),
                                        features: None,
                                        absolute_expiry: None,
                                        paths: None,
@@ -968,6 +998,7 @@ mod tests {
                                        quantity: None,
                                        payer_id: Some(&payer_pubkey()),
                                        payer_note: None,
+                                       paths: None,
                                },
                        ),
                );
@@ -979,7 +1010,7 @@ mod tests {
 
        #[test]
        fn fails_building_refund_with_invalid_amount() {
-               match RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), MAX_VALUE_MSAT + 1) {
+               match RefundBuilder::new(vec![1; 32], payer_pubkey(), MAX_VALUE_MSAT + 1) {
                        Ok(_) => panic!("expected error"),
                        Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidAmount),
                }
@@ -987,7 +1018,6 @@ mod tests {
 
        #[test]
        fn builds_refund_with_metadata_derived() {
-               let desc = "foo".to_string();
                let node_id = payer_pubkey();
                let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
                let entropy = FixedEntropy {};
@@ -995,7 +1025,7 @@ mod tests {
                let payment_id = PaymentId([1; 32]);
 
                let refund = RefundBuilder
-                       ::deriving_payer_id(desc, node_id, &expanded_key, &entropy, &secp_ctx, 1000, payment_id)
+                       ::deriving_payer_id(node_id, &expanded_key, &entropy, &secp_ctx, 1000, payment_id)
                        .unwrap()
                        .build().unwrap();
                assert_eq!(refund.payer_id(), node_id);
@@ -1042,7 +1072,6 @@ mod tests {
 
        #[test]
        fn builds_refund_with_derived_payer_id() {
-               let desc = "foo".to_string();
                let node_id = payer_pubkey();
                let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
                let entropy = FixedEntropy {};
@@ -1050,7 +1079,7 @@ mod tests {
                let payment_id = PaymentId([1; 32]);
 
                let blinded_path = BlindedPath {
-                       introduction_node_id: pubkey(40),
+                       introduction_node: IntroductionNode::NodeId(pubkey(40)),
                        blinding_point: pubkey(41),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
@@ -1059,7 +1088,7 @@ mod tests {
                };
 
                let refund = RefundBuilder
-                       ::deriving_payer_id(desc, node_id, &expanded_key, &entropy, &secp_ctx, 1000, payment_id)
+                       ::deriving_payer_id(node_id, &expanded_key, &entropy, &secp_ctx, 1000, payment_id)
                        .unwrap()
                        .path(blinded_path)
                        .build().unwrap();
@@ -1111,7 +1140,7 @@ mod tests {
                let past_expiry = Duration::from_secs(0);
                let now = future_expiry - Duration::from_secs(1_000);
 
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .absolute_expiry(future_expiry)
                        .build()
                        .unwrap();
@@ -1122,7 +1151,7 @@ mod tests {
                assert_eq!(refund.absolute_expiry(), Some(future_expiry));
                assert_eq!(tlv_stream.absolute_expiry, Some(future_expiry.as_secs()));
 
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .absolute_expiry(future_expiry)
                        .absolute_expiry(past_expiry)
                        .build()
@@ -1139,7 +1168,7 @@ mod tests {
        fn builds_refund_with_paths() {
                let paths = vec![
                        BlindedPath {
-                               introduction_node_id: pubkey(40),
+                               introduction_node: IntroductionNode::NodeId(pubkey(40)),
                                blinding_point: pubkey(41),
                                blinded_hops: vec![
                                        BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
@@ -1147,7 +1176,7 @@ mod tests {
                                ],
                        },
                        BlindedPath {
-                               introduction_node_id: pubkey(40),
+                               introduction_node: IntroductionNode::NodeId(pubkey(40)),
                                blinding_point: pubkey(41),
                                blinded_hops: vec![
                                        BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
@@ -1156,22 +1185,22 @@ mod tests {
                        },
                ];
 
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .path(paths[0].clone())
                        .path(paths[1].clone())
                        .build()
                        .unwrap();
-               let (_, offer_tlv_stream, invoice_request_tlv_stream) = refund.as_tlv_stream();
-               assert_eq!(refund.paths(), paths.as_slice());
+               let (_, _, invoice_request_tlv_stream) = refund.as_tlv_stream();
                assert_eq!(refund.payer_id(), pubkey(42));
+               assert_eq!(refund.paths(), paths.as_slice());
                assert_ne!(pubkey(42), pubkey(44));
-               assert_eq!(offer_tlv_stream.paths, Some(&paths));
                assert_eq!(invoice_request_tlv_stream.payer_id, Some(&pubkey(42)));
+               assert_eq!(invoice_request_tlv_stream.paths, Some(&paths));
        }
 
        #[test]
        fn builds_refund_with_issuer() {
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .issuer("bar".into())
                        .build()
                        .unwrap();
@@ -1179,7 +1208,7 @@ mod tests {
                assert_eq!(refund.issuer(), Some(PrintableString("bar")));
                assert_eq!(tlv_stream.issuer, Some(&String::from("bar")));
 
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .issuer("bar".into())
                        .issuer("baz".into())
                        .build()
@@ -1194,21 +1223,21 @@ mod tests {
                let mainnet = ChainHash::using_genesis_block(Network::Bitcoin);
                let testnet = ChainHash::using_genesis_block(Network::Testnet);
 
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .chain(Network::Bitcoin)
                        .build().unwrap();
                let (_, _, tlv_stream) = refund.as_tlv_stream();
                assert_eq!(refund.chain(), mainnet);
                assert_eq!(tlv_stream.chain, None);
 
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .chain(Network::Testnet)
                        .build().unwrap();
                let (_, _, tlv_stream) = refund.as_tlv_stream();
                assert_eq!(refund.chain(), testnet);
                assert_eq!(tlv_stream.chain, Some(&testnet));
 
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .chain(Network::Regtest)
                        .chain(Network::Testnet)
                        .build().unwrap();
@@ -1219,14 +1248,14 @@ mod tests {
 
        #[test]
        fn builds_refund_with_quantity() {
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .quantity(10)
                        .build().unwrap();
                let (_, _, tlv_stream) = refund.as_tlv_stream();
                assert_eq!(refund.quantity(), Some(10));
                assert_eq!(tlv_stream.quantity, Some(10));
 
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .quantity(10)
                        .quantity(1)
                        .build().unwrap();
@@ -1237,14 +1266,14 @@ mod tests {
 
        #[test]
        fn builds_refund_with_payer_note() {
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .payer_note("bar".into())
                        .build().unwrap();
                let (_, _, tlv_stream) = refund.as_tlv_stream();
                assert_eq!(refund.payer_note(), Some(PrintableString("bar")));
                assert_eq!(tlv_stream.payer_note, Some(&String::from("bar")));
 
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .payer_note("bar".into())
                        .payer_note("baz".into())
                        .build().unwrap();
@@ -1255,7 +1284,7 @@ mod tests {
 
        #[test]
        fn fails_responding_with_unknown_required_features() {
-               match RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               match RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .features_unchecked(InvoiceRequestFeatures::unknown())
                        .build().unwrap()
                        .respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now())
@@ -1267,7 +1296,7 @@ mod tests {
 
        #[test]
        fn parses_refund_with_metadata() {
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .build().unwrap();
                if let Err(e) = refund.to_string().parse::<Refund>() {
                        panic!("error parsing refund: {:?}", e);
@@ -1286,7 +1315,7 @@ mod tests {
 
        #[test]
        fn parses_refund_with_description() {
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .build().unwrap();
                if let Err(e) = refund.to_string().parse::<Refund>() {
                        panic!("error parsing refund: {:?}", e);
@@ -1305,7 +1334,7 @@ mod tests {
 
        #[test]
        fn parses_refund_with_amount() {
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .build().unwrap();
                if let Err(e) = refund.to_string().parse::<Refund>() {
                        panic!("error parsing refund: {:?}", e);
@@ -1334,7 +1363,7 @@ mod tests {
 
        #[test]
        fn parses_refund_with_payer_id() {
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .build().unwrap();
                if let Err(e) = refund.to_string().parse::<Refund>() {
                        panic!("error parsing refund: {:?}", e);
@@ -1356,7 +1385,7 @@ mod tests {
                let past_expiry = Duration::from_secs(0);
                let paths = vec![
                        BlindedPath {
-                               introduction_node_id: pubkey(40),
+                               introduction_node: IntroductionNode::NodeId(pubkey(40)),
                                blinding_point: pubkey(41),
                                blinded_hops: vec![
                                        BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
@@ -1364,7 +1393,7 @@ mod tests {
                                ],
                        },
                        BlindedPath {
-                               introduction_node_id: pubkey(40),
+                               introduction_node: IntroductionNode::NodeId(pubkey(40)),
                                blinding_point: pubkey(41),
                                blinded_hops: vec![
                                        BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
@@ -1373,7 +1402,7 @@ mod tests {
                        },
                ];
 
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .absolute_expiry(past_expiry)
                        .issuer("bar".into())
                        .path(paths[0].clone())
@@ -1402,7 +1431,7 @@ mod tests {
 
        #[test]
        fn fails_parsing_refund_with_unexpected_fields() {
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()
+               let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap()
                        .build().unwrap();
                if let Err(e) = refund.to_string().parse::<Refund>() {
                        panic!("error parsing refund: {:?}", e);
@@ -1477,8 +1506,8 @@ mod tests {
        #[test]
        fn fails_parsing_refund_with_extra_tlv_records() {
                let secp_ctx = Secp256k1::new();
-               let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
-               let refund = RefundBuilder::new("foo".into(), vec![1; 32], keys.public_key(), 1000).unwrap()
+               let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+               let refund = RefundBuilder::new(vec![1; 32], keys.public_key(), 1000).unwrap()
                        .build().unwrap();
 
                let mut encoded_refund = Vec::new();
index e0abd82767f6c9fd51dc0a7e425f7236631a0f67..fff0514564ca34253d8a2b958e95ba1a9e8f4549 100644 (file)
@@ -13,8 +13,7 @@ use bitcoin::hashes::{Hash, HashEngine};
 use bitcoin::hashes::cmp::fixed_time_eq;
 use bitcoin::hashes::hmac::{Hmac, HmacEngine};
 use bitcoin::hashes::sha256::Hash as Sha256;
-use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey, self};
-use core::convert::TryFrom;
+use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, SecretKey, self};
 use core::fmt;
 use crate::ln::channelmanager::PaymentId;
 use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
@@ -103,7 +102,7 @@ impl Metadata {
 
        pub fn derive_from<W: Writeable, T: secp256k1::Signing>(
                self, tlv_stream: W, secp_ctx: Option<&Secp256k1<T>>
-       ) -> (Self, Option<KeyPair>) {
+       ) -> (Self, Option<Keypair>) {
                match self {
                        Metadata::Bytes(_) => (self, None),
                        Metadata::Derived(mut metadata_material) => {
@@ -189,7 +188,7 @@ impl MetadataMaterial {
 
        fn derive_metadata_and_keys<T: secp256k1::Signing>(
                mut self, secp_ctx: &Secp256k1<T>
-       ) -> (Vec<u8>, KeyPair) {
+       ) -> (Vec<u8>, Keypair) {
                self.hmac.input(DERIVED_METADATA_AND_KEYS_HMAC_INPUT);
                self.maybe_include_encrypted_payment_id();
 
@@ -198,7 +197,7 @@ impl MetadataMaterial {
 
                let hmac = Hmac::from_engine(self.hmac);
                let privkey = SecretKey::from_slice(hmac.as_byte_array()).unwrap();
-               let keys = KeyPair::from_secret_key(secp_ctx, &privkey);
+               let keys = Keypair::from_secret_key(secp_ctx, &privkey);
 
                (bytes, keys)
        }
@@ -214,12 +213,12 @@ impl MetadataMaterial {
        }
 }
 
-pub(super) fn derive_keys(nonce: Nonce, expanded_key: &ExpandedKey) -> KeyPair {
+pub(super) fn derive_keys(nonce: Nonce, expanded_key: &ExpandedKey) -> Keypair {
        const IV_BYTES: &[u8; IV_LEN] = b"LDK Invoice ~~~~";
        let secp_ctx = Secp256k1::new();
        let hmac = Hmac::from_engine(expanded_key.hmac_for_offer(nonce, IV_BYTES));
        let privkey = SecretKey::from_slice(hmac.as_byte_array()).unwrap();
-       KeyPair::from_secret_key(&secp_ctx, &privkey)
+       Keypair::from_secret_key(&secp_ctx, &privkey)
 }
 
 /// Verifies data given in a TLV stream was used to produce the given metadata, consisting of:
@@ -266,12 +265,12 @@ pub(super) fn verify_payer_metadata<'a, T: secp256k1::Signing>(
 /// If the latter is not included in the metadata, the TLV stream is used to check if the given
 /// `signing_pubkey` can be derived from it.
 ///
-/// Returns the [`KeyPair`] for signing the invoice, if it can be derived from the metadata.
+/// Returns the [`Keypair`] for signing the invoice, if it can be derived from the metadata.
 pub(super) fn verify_recipient_metadata<'a, T: secp256k1::Signing>(
        metadata: &[u8], expanded_key: &ExpandedKey, iv_bytes: &[u8; IV_LEN],
        signing_pubkey: PublicKey, tlv_stream: impl core::iter::Iterator<Item = TlvRecord<'a>>,
        secp_ctx: &Secp256k1<T>
-) -> Result<Option<KeyPair>, ()> {
+) -> Result<Option<Keypair>, ()> {
        let mut hmac = hmac_for_message(metadata, expanded_key, iv_bytes, tlv_stream)?;
        hmac.input(WITHOUT_ENCRYPTED_PAYMENT_ID_HMAC_INPUT);
 
@@ -280,9 +279,9 @@ pub(super) fn verify_recipient_metadata<'a, T: secp256k1::Signing>(
 
 fn verify_metadata<T: secp256k1::Signing>(
        metadata: &[u8], hmac: Hmac<Sha256>, signing_pubkey: PublicKey, secp_ctx: &Secp256k1<T>
-) -> Result<Option<KeyPair>, ()> {
+) -> Result<Option<Keypair>, ()> {
        if metadata.len() == Nonce::LENGTH {
-               let derived_keys = KeyPair::from_secret_key(
+               let derived_keys = Keypair::from_secret_key(
                        secp_ctx, &SecretKey::from_slice(hmac.as_byte_array()).unwrap()
                );
                if fixed_time_eq(&signing_pubkey.serialize(), &derived_keys.public_key().serialize()) {
index 29bed53d83f561f030e4966373c4403020ac6ffc..0c35ee2859878a7bb98171481b5c1fc4132fed2b 100644 (file)
@@ -9,29 +9,32 @@
 
 //! Utilities for testing BOLT 12 Offers interfaces
 
-use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
+use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, SecretKey};
 use bitcoin::secp256k1::schnorr::Signature;
-use core::convert::AsRef; 
+
 use core::time::Duration;
-use crate::blinded_path::{BlindedHop, BlindedPath};
+use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
 use crate::sign::EntropySource;
-use crate::ln::PaymentHash;
+use crate::ln::types::PaymentHash;
 use crate::ln::features::BlindedHopFeatures;
 use crate::offers::invoice::BlindedPayInfo;
 use crate::offers::merkle::TaggedHash;
 
+#[allow(unused_imports)]
+use crate::prelude::*;
+
 pub(crate) fn fail_sign<T: AsRef<TaggedHash>>(_message: &T) -> Result<Signature, ()> {
        Err(())
 }
 
-pub(crate) fn payer_keys() -> KeyPair {
+pub(crate) fn payer_keys() -> Keypair {
        let secp_ctx = Secp256k1::new();
-       KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap())
+       Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap())
 }
 
 pub(crate) fn payer_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, ()> {
        let secp_ctx = Secp256k1::new();
-       let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+       let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
        Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
 }
 
@@ -39,14 +42,14 @@ pub(crate) fn payer_pubkey() -> PublicKey {
        payer_keys().public_key()
 }
 
-pub(crate) fn recipient_keys() -> KeyPair {
+pub(crate) fn recipient_keys() -> Keypair {
        let secp_ctx = Secp256k1::new();
-       KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap())
+       Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap())
 }
 
 pub(crate) fn recipient_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, ()> {
        let secp_ctx = Secp256k1::new();
-       let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap());
+       let keys = Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap());
        Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
 }
 
@@ -66,7 +69,7 @@ pub(super) fn privkey(byte: u8) -> SecretKey {
 pub(crate) fn payment_paths() -> Vec<(BlindedPayInfo, BlindedPath)> {
        let paths = vec![
                BlindedPath {
-                       introduction_node_id: pubkey(40),
+                       introduction_node: IntroductionNode::NodeId(pubkey(40)),
                        blinding_point: pubkey(41),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
@@ -74,7 +77,7 @@ pub(crate) fn payment_paths() -> Vec<(BlindedPayInfo, BlindedPath)> {
                        ],
                },
                BlindedPath {
-                       introduction_node_id: pubkey(40),
+                       introduction_node: IntroductionNode::NodeId(pubkey(40)),
                        blinding_point: pubkey(41),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
index 271b56dc0c92a7a2de07e7d3b6c652777eb608d0..08be1b2c5027d64bdf599faca92f0b2ed510ef5c 100644 (file)
 
 //! Onion message testing and test utilities live here.
 
-use crate::blinded_path::BlindedPath;
+use crate::blinded_path::{BlindedPath, EmptyNodeIdLookUp};
+use crate::blinded_path::message::ForwardNode;
 use crate::events::{Event, EventsProvider};
-use crate::ln::features::InitFeatures;
-use crate::ln::msgs::{self, DecodeError, OnionMessageHandler, SocketAddress};
+use crate::ln::features::{ChannelFeatures, InitFeatures};
+use crate::ln::msgs::{self, DecodeError, OnionMessageHandler};
+use crate::routing::gossip::{NetworkGraph, P2PGossipSync};
+use crate::routing::test_utils::{add_channel, add_or_update_node};
 use crate::sign::{NodeSigner, Recipient};
 use crate::util::ser::{FixedLengthReader, LengthReadable, Writeable, Writer};
 use crate::util::test_utils;
-use super::messenger::{CustomOnionMessageHandler, Destination, MessageRouter, OnionMessagePath, OnionMessenger, PendingOnionMessage, SendError};
+use super::messenger::{CustomOnionMessageHandler, DefaultMessageRouter, Destination, OnionMessagePath, OnionMessenger, PendingOnionMessage, Responder, ResponseInstruction, SendError, SendSuccess};
 use super::offers::{OffersMessage, OffersMessageHandler};
 use super::packet::{OnionMessageContents, Packet};
 
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 use bitcoin::hashes::hex::FromHex;
-use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey, self};
+use bitcoin::secp256k1::{All, PublicKey, Secp256k1, SecretKey};
 
 use crate::io;
 use crate::io_extras::read_to_end;
 use crate::sync::{Arc, Mutex};
 
+use core::ops::Deref;
+
 use crate::prelude::*;
 
 struct MessengerNode {
        node_id: PublicKey,
+       privkey: SecretKey,
        entropy_source: Arc<test_utils::TestKeysInterface>,
        messenger: OnionMessenger<
                Arc<test_utils::TestKeysInterface>,
                Arc<test_utils::TestNodeSigner>,
                Arc<test_utils::TestLogger>,
-               Arc<TestMessageRouter>,
+               Arc<EmptyNodeIdLookUp>,
+               Arc<DefaultMessageRouter<
+                       Arc<NetworkGraph<Arc<test_utils::TestLogger>>>,
+                       Arc<test_utils::TestLogger>,
+                       Arc<test_utils::TestKeysInterface>
+               >>,
                Arc<TestOffersMessageHandler>,
                Arc<TestCustomMessageHandler>
        >,
        custom_message_handler: Arc<TestCustomMessageHandler>,
+       gossip_sync: Arc<P2PGossipSync<
+               Arc<NetworkGraph<Arc<test_utils::TestLogger>>>,
+               Arc<test_utils::TestChainSource>,
+               Arc<test_utils::TestLogger>
+       >>
 }
 
-struct TestMessageRouter {}
-
-impl MessageRouter for TestMessageRouter {
-       fn find_path(
-               &self, _sender: PublicKey, _peers: Vec<PublicKey>, destination: Destination
-       ) -> Result<OnionMessagePath, ()> {
-               Ok(OnionMessagePath {
-                       intermediate_nodes: vec![],
-                       destination,
-                       first_node_addresses:
-                               Some(vec![SocketAddress::TcpIpV4 { addr: [127, 0, 0, 1], port: 1000 }]),
-               })
-       }
-
-       fn create_blinded_paths<
-               T: secp256k1::Signing + secp256k1::Verification
-       >(
-               &self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
-       ) -> Result<Vec<BlindedPath>, ()> {
-               unreachable!()
+impl Drop for MessengerNode {
+       fn drop(&mut self) {
+               #[cfg(feature = "std")] {
+                       if std::thread::panicking() {
+                               return;
+                       }
+               }
+               assert!(release_events(self).is_empty());
        }
 }
 
 struct TestOffersMessageHandler {}
 
 impl OffersMessageHandler for TestOffersMessageHandler {
-       fn handle_message(&self, _message: OffersMessage) -> Option<OffersMessage> {
-               None
+       fn handle_message(&self, _message: OffersMessage, _responder: Option<Responder>) -> ResponseInstruction<OffersMessage> {
+               ResponseInstruction::NoResponse
        }
 }
 
 #[derive(Clone, Debug, PartialEq)]
 enum TestCustomMessage {
-       Request,
-       Response,
+       Ping,
+       Pong,
 }
 
-const CUSTOM_REQUEST_MESSAGE_TYPE: u64 = 4242;
-const CUSTOM_RESPONSE_MESSAGE_TYPE: u64 = 4343;
-const CUSTOM_REQUEST_MESSAGE_CONTENTS: [u8; 32] = [42; 32];
-const CUSTOM_RESPONSE_MESSAGE_CONTENTS: [u8; 32] = [43; 32];
+const CUSTOM_PING_MESSAGE_TYPE: u64 = 4242;
+const CUSTOM_PONG_MESSAGE_TYPE: u64 = 4343;
+const CUSTOM_PING_MESSAGE_CONTENTS: [u8; 32] = [42; 32];
+const CUSTOM_PONG_MESSAGE_CONTENTS: [u8; 32] = [43; 32];
 
 impl OnionMessageContents for TestCustomMessage {
        fn tlv_type(&self) -> u64 {
                match self {
-                       TestCustomMessage::Request => CUSTOM_REQUEST_MESSAGE_TYPE,
-                       TestCustomMessage::Response => CUSTOM_RESPONSE_MESSAGE_TYPE,
+                       TestCustomMessage::Ping => CUSTOM_PING_MESSAGE_TYPE,
+                       TestCustomMessage::Pong => CUSTOM_PONG_MESSAGE_TYPE,
                }
        }
+       fn msg_type(&self) -> &'static str {
+               "Custom Message"
+       }
 }
 
 impl Writeable for TestCustomMessage {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
                match self {
-                       TestCustomMessage::Request => Ok(CUSTOM_REQUEST_MESSAGE_CONTENTS.write(w)?),
-                       TestCustomMessage::Response => Ok(CUSTOM_RESPONSE_MESSAGE_CONTENTS.write(w)?),
+                       TestCustomMessage::Ping => Ok(CUSTOM_PING_MESSAGE_CONTENTS.write(w)?),
+                       TestCustomMessage::Pong => Ok(CUSTOM_PONG_MESSAGE_CONTENTS.write(w)?),
                }
        }
 }
 
 struct TestCustomMessageHandler {
-       expected_messages: Mutex<VecDeque<TestCustomMessage>>,
+       expectations: Mutex<VecDeque<OnHandleCustomMessage>>,
+}
+
+struct OnHandleCustomMessage {
+       expect: TestCustomMessage,
+       include_reply_path: bool,
 }
 
 impl TestCustomMessageHandler {
        fn new() -> Self {
-               Self { expected_messages: Mutex::new(VecDeque::new()) }
+               Self { expectations: Mutex::new(VecDeque::new()) }
        }
 
        fn expect_message(&self, message: TestCustomMessage) {
-               self.expected_messages.lock().unwrap().push_back(message);
+               self.expectations.lock().unwrap().push_back(
+                       OnHandleCustomMessage {
+                               expect: message,
+                               include_reply_path: false,
+                       }
+               );
+       }
+
+       fn expect_message_and_response(&self, message: TestCustomMessage) {
+               self.expectations.lock().unwrap().push_back(
+                       OnHandleCustomMessage {
+                               expect: message,
+                               include_reply_path: true,
+                       }
+               );
+       }
+
+       fn get_next_expectation(&self) -> OnHandleCustomMessage {
+               self.expectations.lock().unwrap().pop_front().expect("No expectations remaining")
        }
 }
 
@@ -125,34 +155,45 @@ impl Drop for TestCustomMessageHandler {
                                return;
                        }
                }
-               assert!(self.expected_messages.lock().unwrap().is_empty());
+               assert!(self.expectations.lock().unwrap().is_empty());
        }
 }
 
 impl CustomOnionMessageHandler for TestCustomMessageHandler {
        type CustomMessage = TestCustomMessage;
-       fn handle_custom_message(&self, msg: Self::CustomMessage) -> Option<Self::CustomMessage> {
-               match self.expected_messages.lock().unwrap().pop_front() {
-                       Some(expected_msg) => assert_eq!(expected_msg, msg),
-                       None => panic!("Unexpected message: {:?}", msg),
+       fn handle_custom_message(&self, msg: Self::CustomMessage, responder: Option<Responder>) -> ResponseInstruction<Self::CustomMessage> {
+               let expectation = self.get_next_expectation();
+               assert_eq!(msg, expectation.expect);
+
+               let response = match msg {
+                       TestCustomMessage::Ping => TestCustomMessage::Pong,
+                       TestCustomMessage::Pong => TestCustomMessage::Ping,
+               };
+
+               // Sanity check: expecting to include reply path when responder is absent should panic.
+               if expectation.include_reply_path && responder.is_none() {
+                       panic!("Expected to include a reply_path, but the responder was absent.")
                }
 
-               match msg {
-                       TestCustomMessage::Request => Some(TestCustomMessage::Response),
-                       TestCustomMessage::Response => None,
+               match responder {
+                       Some(responder) if expectation.include_reply_path => {
+                               responder.respond_with_reply_path(response)
+                       },
+                       Some(responder) => responder.respond(response),
+                       None => ResponseInstruction::NoResponse,
                }
        }
        fn read_custom_message<R: io::Read>(&self, message_type: u64, buffer: &mut R) -> Result<Option<Self::CustomMessage>, DecodeError> where Self: Sized {
                match message_type {
-                       CUSTOM_REQUEST_MESSAGE_TYPE => {
+                       CUSTOM_PING_MESSAGE_TYPE => {
                                let buf = read_to_end(buffer)?;
-                               assert_eq!(buf, CUSTOM_REQUEST_MESSAGE_CONTENTS);
-                               Ok(Some(TestCustomMessage::Request))
+                               assert_eq!(buf, CUSTOM_PING_MESSAGE_CONTENTS);
+                               Ok(Some(TestCustomMessage::Ping))
                        },
-                       CUSTOM_RESPONSE_MESSAGE_TYPE => {
+                       CUSTOM_PONG_MESSAGE_TYPE => {
                                let buf = read_to_end(buffer)?;
-                               assert_eq!(buf, CUSTOM_RESPONSE_MESSAGE_CONTENTS);
-                               Ok(Some(TestCustomMessage::Response))
+                               assert_eq!(buf, CUSTOM_PONG_MESSAGE_CONTENTS);
+                               Ok(Some(TestCustomMessage::Pong))
                        },
                        _ => Ok(None),
                }
@@ -163,32 +204,72 @@ impl CustomOnionMessageHandler for TestCustomMessageHandler {
 }
 
 fn create_nodes(num_messengers: u8) -> Vec<MessengerNode> {
-       let secrets = (1..=num_messengers)
+       let cfgs = (1..=num_messengers)
                .into_iter()
-               .map(|i| SecretKey::from_slice(&[i; 32]).unwrap())
+               .map(|_| MessengerCfg::new())
                .collect();
-       create_nodes_using_secrets(secrets)
+       create_nodes_using_cfgs(cfgs)
 }
 
-fn create_nodes_using_secrets(secrets: Vec<SecretKey>) -> Vec<MessengerNode> {
+struct MessengerCfg {
+       secret_override: Option<SecretKey>,
+       intercept_offline_peer_oms: bool,
+}
+impl MessengerCfg {
+       fn new() -> Self {
+               Self { secret_override: None, intercept_offline_peer_oms: false }
+       }
+       fn with_node_secret(mut self, secret: SecretKey) -> Self {
+               self.secret_override = Some(secret);
+               self
+       }
+       fn with_offline_peer_interception(mut self) -> Self {
+               self.intercept_offline_peer_oms = true;
+               self
+       }
+}
+
+fn create_nodes_using_cfgs(cfgs: Vec<MessengerCfg>) -> Vec<MessengerNode> {
+       let gossip_logger = Arc::new(test_utils::TestLogger::with_id("gossip".to_string()));
+       let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, gossip_logger.clone()));
+       let gossip_sync = Arc::new(
+               P2PGossipSync::new(network_graph.clone(), None, gossip_logger)
+       );
+
        let mut nodes = Vec::new();
-       for (i, secret_key) in secrets.into_iter().enumerate() {
+       for (i, cfg) in cfgs.into_iter().enumerate() {
+               let secret_key = cfg.secret_override.unwrap_or(SecretKey::from_slice(&[(i + 1) as u8; 32]).unwrap());
                let logger = Arc::new(test_utils::TestLogger::with_id(format!("node {}", i)));
                let seed = [i as u8; 32];
                let entropy_source = Arc::new(test_utils::TestKeysInterface::new(&seed, Network::Testnet));
                let node_signer = Arc::new(test_utils::TestNodeSigner::new(secret_key));
 
-               let message_router = Arc::new(TestMessageRouter {});
+               let node_id_lookup = Arc::new(EmptyNodeIdLookUp {});
+               let message_router = Arc::new(
+                       DefaultMessageRouter::new(network_graph.clone(), entropy_source.clone())
+               );
                let offers_message_handler = Arc::new(TestOffersMessageHandler {});
                let custom_message_handler = Arc::new(TestCustomMessageHandler::new());
+               let messenger = if cfg.intercept_offline_peer_oms {
+                       OnionMessenger::new_with_offline_peer_interception(
+                               entropy_source.clone(), node_signer.clone(), logger.clone(),
+                               node_id_lookup, message_router, offers_message_handler,
+                               custom_message_handler.clone()
+                       )
+               } else {
+                       OnionMessenger::new(
+                               entropy_source.clone(), node_signer.clone(), logger.clone(),
+                               node_id_lookup, message_router, offers_message_handler,
+                               custom_message_handler.clone()
+                       )
+               };
                nodes.push(MessengerNode {
+                       privkey: secret_key,
                        node_id: node_signer.get_node_id(Recipient::Node).unwrap(),
-                       entropy_source: entropy_source.clone(),
-                       messenger: OnionMessenger::new(
-                               entropy_source, node_signer, logger.clone(), message_router,
-                               offers_message_handler, custom_message_handler.clone()
-                       ),
+                       entropy_source,
+                       messenger,
                        custom_message_handler,
+                       gossip_sync: gossip_sync.clone(),
                });
        }
        for i in 0..nodes.len() - 1 {
@@ -216,6 +297,20 @@ fn release_events(node: &MessengerNode) -> Vec<Event> {
        events.into_inner()
 }
 
+fn add_channel_to_graph(
+       node_a: &MessengerNode, node_b: &MessengerNode, secp_ctx: &Secp256k1<All>, short_channel_id: u64
+) {
+       let gossip_sync = node_a.gossip_sync.deref();
+       let privkey_a = &node_a.privkey;
+       let privkey_b = &node_b.privkey;
+       let channel_features = ChannelFeatures::empty();
+       let node_features_a = node_a.messenger.provided_node_features();
+       let node_features_b = node_b.messenger.provided_node_features();
+       add_channel(gossip_sync, secp_ctx, privkey_a, privkey_b, channel_features, short_channel_id);
+       add_or_update_node(gossip_sync, secp_ctx, privkey_a, node_features_a, 1);
+       add_or_update_node(gossip_sync, secp_ctx, privkey_b, node_features_b, 1);
+}
+
 fn pass_along_path(path: &Vec<MessengerNode>) {
        let mut prev_node = &path[0];
        for node in path.into_iter().skip(1) {
@@ -233,57 +328,51 @@ fn pass_along_path(path: &Vec<MessengerNode>) {
 #[test]
 fn one_unblinded_hop() {
        let nodes = create_nodes(2);
-       let test_msg = TestCustomMessage::Response;
+       let test_msg = TestCustomMessage::Pong;
 
-       let path = OnionMessagePath {
-               intermediate_nodes: vec![],
-               destination: Destination::Node(nodes[1].node_id),
-               first_node_addresses: None,
-       };
-       nodes[0].messenger.send_onion_message_using_path(path, test_msg, None).unwrap();
-       nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response);
+       let destination = Destination::Node(nodes[1].node_id);
+       nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap();
+       nodes[1].custom_message_handler.expect_message(TestCustomMessage::Pong);
        pass_along_path(&nodes);
 }
 
 #[test]
 fn two_unblinded_hops() {
        let nodes = create_nodes(3);
-       let test_msg = TestCustomMessage::Response;
+       let test_msg = TestCustomMessage::Pong;
 
        let path = OnionMessagePath {
                intermediate_nodes: vec![nodes[1].node_id],
                destination: Destination::Node(nodes[2].node_id),
                first_node_addresses: None,
        };
+
        nodes[0].messenger.send_onion_message_using_path(path, test_msg, None).unwrap();
-       nodes[2].custom_message_handler.expect_message(TestCustomMessage::Response);
+       nodes[2].custom_message_handler.expect_message(TestCustomMessage::Pong);
        pass_along_path(&nodes);
 }
 
 #[test]
 fn one_blinded_hop() {
        let nodes = create_nodes(2);
-       let test_msg = TestCustomMessage::Response;
+       let test_msg = TestCustomMessage::Pong;
 
        let secp_ctx = Secp256k1::new();
-       let blinded_path = BlindedPath::new_for_message(&[nodes[1].node_id], &*nodes[1].entropy_source, &secp_ctx).unwrap();
-       let path = OnionMessagePath {
-               intermediate_nodes: vec![],
-               destination: Destination::BlindedPath(blinded_path),
-               first_node_addresses: None,
-       };
-       nodes[0].messenger.send_onion_message_using_path(path, test_msg, None).unwrap();
-       nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response);
+       let blinded_path = BlindedPath::new_for_message(&[], nodes[1].node_id, &*nodes[1].entropy_source, &secp_ctx).unwrap();
+       let destination = Destination::BlindedPath(blinded_path);
+       nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap();
+       nodes[1].custom_message_handler.expect_message(TestCustomMessage::Pong);
        pass_along_path(&nodes);
 }
 
 #[test]
 fn two_unblinded_two_blinded() {
        let nodes = create_nodes(5);
-       let test_msg = TestCustomMessage::Response;
+       let test_msg = TestCustomMessage::Pong;
 
        let secp_ctx = Secp256k1::new();
-       let blinded_path = BlindedPath::new_for_message(&[nodes[3].node_id, nodes[4].node_id], &*nodes[4].entropy_source, &secp_ctx).unwrap();
+       let intermediate_nodes = [ForwardNode { node_id: nodes[3].node_id, short_channel_id: None }];
+       let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[4].node_id, &*nodes[4].entropy_source, &secp_ctx).unwrap();
        let path = OnionMessagePath {
                intermediate_nodes: vec![nodes[1].node_id, nodes[2].node_id],
                destination: Destination::BlindedPath(blinded_path),
@@ -291,33 +380,135 @@ fn two_unblinded_two_blinded() {
        };
 
        nodes[0].messenger.send_onion_message_using_path(path, test_msg, None).unwrap();
-       nodes[4].custom_message_handler.expect_message(TestCustomMessage::Response);
+       nodes[4].custom_message_handler.expect_message(TestCustomMessage::Pong);
        pass_along_path(&nodes);
 }
 
 #[test]
 fn three_blinded_hops() {
        let nodes = create_nodes(4);
-       let test_msg = TestCustomMessage::Response;
+       let test_msg = TestCustomMessage::Pong;
 
        let secp_ctx = Secp256k1::new();
-       let blinded_path = BlindedPath::new_for_message(&[nodes[1].node_id, nodes[2].node_id, nodes[3].node_id], &*nodes[3].entropy_source, &secp_ctx).unwrap();
-       let path = OnionMessagePath {
-               intermediate_nodes: vec![],
-               destination: Destination::BlindedPath(blinded_path),
-               first_node_addresses: None,
-       };
+       let intermediate_nodes = [
+               ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
+               ForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
+       ];
+       let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[3].node_id, &*nodes[3].entropy_source, &secp_ctx).unwrap();
+       let destination = Destination::BlindedPath(blinded_path);
 
-       nodes[0].messenger.send_onion_message_using_path(path, test_msg, None).unwrap();
-       nodes[3].custom_message_handler.expect_message(TestCustomMessage::Response);
+       nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap();
+       nodes[3].custom_message_handler.expect_message(TestCustomMessage::Pong);
+       pass_along_path(&nodes);
+}
+
+#[test]
+fn async_response_over_one_blinded_hop() {
+       // Simulate an asynchronous interaction between two nodes, Alice and Bob.
+
+       // 1. Set up the network with two nodes: Alice and Bob.
+       let nodes = create_nodes(2);
+       let alice = &nodes[0];
+       let bob = &nodes[1];
+
+       // 2. Define the message sent from Bob to Alice.
+       let message = TestCustomMessage::Ping;
+       let path_id = Some([2; 32]);
+
+       // 3. Simulate the creation of a Blinded Reply path provided by Bob.
+       let secp_ctx = Secp256k1::new();
+       let reply_path = BlindedPath::new_for_message(&[], nodes[1].node_id, &*nodes[1].entropy_source, &secp_ctx).unwrap();
+
+       // 4. Create a responder using the reply path for Alice.
+       let responder = Some(Responder::new(reply_path, path_id));
+
+       // 5. Expect Alice to receive the message and create a response instruction for it.
+       alice.custom_message_handler.expect_message(message.clone());
+       let response_instruction = nodes[0].custom_message_handler.handle_custom_message(message, responder);
+
+       // 6. Simulate Alice asynchronously responding back to Bob with a response.
+       assert_eq!(
+               nodes[0].messenger.handle_onion_message_response(response_instruction),
+               Ok(Some(SendSuccess::Buffered)),
+       );
+
+       bob.custom_message_handler.expect_message(TestCustomMessage::Pong);
+
+       pass_along_path(&nodes);
+}
+
+#[test]
+fn async_response_with_reply_path_succeeds() {
+       // Simulate an asynchronous interaction between two nodes, Alice and Bob.
+       // Create a channel between the two nodes to establish them as announced nodes,
+       // which allows the creation of the reply_path for successful communication.
+
+       let mut nodes = create_nodes(2);
+       let alice = &nodes[0];
+       let bob = &nodes[1];
+       let secp_ctx = Secp256k1::new();
+
+       add_channel_to_graph(alice, bob, &secp_ctx, 24);
+
+       // Alice receives a message from Bob with an added reply_path for responding back.
+       let message = TestCustomMessage::Ping;
+       let path_id = Some([2; 32]);
+       let reply_path = BlindedPath::new_for_message(&[], bob.node_id, &*bob.entropy_source, &secp_ctx).unwrap();
+
+       // Alice asynchronously responds to Bob, expecting a response back from him.
+       let responder = Responder::new(reply_path, path_id);
+       alice.custom_message_handler.expect_message_and_response(message.clone());
+       let response_instruction = alice.custom_message_handler.handle_custom_message(message, Some(responder));
+
+       assert_eq!(
+               alice.messenger.handle_onion_message_response(response_instruction),
+               Ok(Some(SendSuccess::Buffered)),
+       );
+
+       // Set Bob's expectation and pass the Onion Message along the path.
+       bob.custom_message_handler.expect_message(TestCustomMessage::Pong);
+       pass_along_path(&nodes);
+
+       // Bob responds back to Alice using the reply_path she included with the OnionMessage.
+       // Set Alice's expectation and reverse the path for the response.
+       alice.custom_message_handler.expect_message(TestCustomMessage::Ping);
+       nodes.reverse();
        pass_along_path(&nodes);
 }
 
+#[test]
+fn async_response_with_reply_path_fails() {
+       // Simulate an asynchronous interaction between two unannounced nodes, Alice and Bob.
+       // Since the nodes are unannounced, attempting to respond using a reply_path
+       // will fail, leading to an expected failure in communication.
+
+       let nodes = create_nodes(2);
+       let alice = &nodes[0];
+       let bob = &nodes[1];
+       let secp_ctx = Secp256k1::new();
+
+       // Alice receives a message from Bob with an added reply_path for responding back.
+       let message = TestCustomMessage::Ping;
+       let path_id = Some([2; 32]);
+       let reply_path = BlindedPath::new_for_message(&[], bob.node_id, &*bob.entropy_source, &secp_ctx).unwrap();
+
+       // Alice tries to asynchronously respond to Bob, but fails because the nodes are unannounced.
+       // Therefore, the reply_path cannot be used for the response.
+       let responder = Responder::new(reply_path, path_id);
+       alice.custom_message_handler.expect_message_and_response(message.clone());
+       let response_instruction = alice.custom_message_handler.handle_custom_message(message, Some(responder));
+
+       assert_eq!(
+               alice.messenger.handle_onion_message_response(response_instruction),
+               Err(SendError::PathNotFound),
+       );
+}
+
 #[test]
 fn too_big_packet_error() {
        // Make sure we error as expected if a packet is too big to send.
        let nodes = create_nodes(2);
-       let test_msg = TestCustomMessage::Response;
+       let test_msg = TestCustomMessage::Pong;
 
        let hop_node_id = nodes[1].node_id;
        let hops = vec![hop_node_id; 400];
@@ -335,56 +526,49 @@ fn we_are_intro_node() {
        // If we are sending straight to a blinded path and we are the introduction node, we need to
        // advance the blinded path by 1 hop so the second hop is the new introduction node.
        let mut nodes = create_nodes(3);
-       let test_msg = TestCustomMessage::Response;
+       let test_msg = TestCustomMessage::Pong;
 
        let secp_ctx = Secp256k1::new();
-       let blinded_path = BlindedPath::new_for_message(&[nodes[0].node_id, nodes[1].node_id, nodes[2].node_id], &*nodes[2].entropy_source, &secp_ctx).unwrap();
-       let path = OnionMessagePath {
-               intermediate_nodes: vec![],
-               destination: Destination::BlindedPath(blinded_path),
-               first_node_addresses: None,
-       };
+       let intermediate_nodes = [
+               ForwardNode { node_id: nodes[0].node_id, short_channel_id: None },
+               ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
+       ];
+       let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[2].node_id, &*nodes[2].entropy_source, &secp_ctx).unwrap();
+       let destination = Destination::BlindedPath(blinded_path);
 
-       nodes[0].messenger.send_onion_message_using_path(path, test_msg.clone(), None).unwrap();
-       nodes[2].custom_message_handler.expect_message(TestCustomMessage::Response);
+       nodes[0].messenger.send_onion_message(test_msg.clone(), destination, None).unwrap();
+       nodes[2].custom_message_handler.expect_message(TestCustomMessage::Pong);
        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].node_id, nodes[1].node_id], &*nodes[1].entropy_source, &secp_ctx).unwrap();
-       let path = OnionMessagePath {
-               intermediate_nodes: vec![],
-               destination: Destination::BlindedPath(blinded_path),
-               first_node_addresses: None,
-       };
-       nodes[0].messenger.send_onion_message_using_path(path, test_msg, None).unwrap();
-       nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response);
+       let intermediate_nodes = [ForwardNode { node_id: nodes[0].node_id, short_channel_id: None }];
+       let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[1].node_id, &*nodes[1].entropy_source, &secp_ctx).unwrap();
+       let destination = Destination::BlindedPath(blinded_path);
+       nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap();
+       nodes[1].custom_message_handler.expect_message(TestCustomMessage::Pong);
        nodes.remove(2);
        pass_along_path(&nodes);
 }
 
 #[test]
 fn invalid_blinded_path_error() {
-       // Make sure we error as expected if a provided blinded path has 0 or 1 hops.
+       // Make sure we error as expected if a provided blinded path has 0 hops.
        let nodes = create_nodes(3);
-       let test_msg = TestCustomMessage::Response;
+       let test_msg = TestCustomMessage::Pong;
 
-       // 0 hops
        let secp_ctx = Secp256k1::new();
-       let mut blinded_path = BlindedPath::new_for_message(&[nodes[1].node_id, nodes[2].node_id], &*nodes[2].entropy_source, &secp_ctx).unwrap();
+       let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
+       let mut blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[2].node_id, &*nodes[2].entropy_source, &secp_ctx).unwrap();
        blinded_path.blinded_hops.clear();
-       let path = OnionMessagePath {
-               intermediate_nodes: vec![],
-               destination: Destination::BlindedPath(blinded_path),
-               first_node_addresses: None,
-       };
-       let err = nodes[0].messenger.send_onion_message_using_path(path, test_msg.clone(), None).unwrap_err();
+       let destination = Destination::BlindedPath(blinded_path);
+       let err = nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap_err();
        assert_eq!(err, SendError::TooFewBlindedHops);
 }
 
 #[test]
 fn reply_path() {
        let mut nodes = create_nodes(4);
-       let test_msg = TestCustomMessage::Request;
+       let test_msg = TestCustomMessage::Ping;
        let secp_ctx = Secp256k1::new();
 
        // Destination::Node
@@ -393,30 +577,38 @@ fn reply_path() {
                destination: Destination::Node(nodes[3].node_id),
                first_node_addresses: None,
        };
-       let reply_path = BlindedPath::new_for_message(&[nodes[2].node_id, nodes[1].node_id, nodes[0].node_id], &*nodes[0].entropy_source, &secp_ctx).unwrap();
+       let intermediate_nodes = [
+               ForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
+               ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
+       ];
+       let reply_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[0].node_id, &*nodes[0].entropy_source, &secp_ctx).unwrap();
        nodes[0].messenger.send_onion_message_using_path(path, test_msg.clone(), Some(reply_path)).unwrap();
-       nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request);
+       nodes[3].custom_message_handler.expect_message(TestCustomMessage::Ping);
        pass_along_path(&nodes);
        // Make sure the last node successfully decoded the reply path.
-       nodes[0].custom_message_handler.expect_message(TestCustomMessage::Response);
+       nodes[0].custom_message_handler.expect_message(TestCustomMessage::Pong);
        nodes.reverse();
        pass_along_path(&nodes);
 
        // Destination::BlindedPath
-       let blinded_path = BlindedPath::new_for_message(&[nodes[1].node_id, nodes[2].node_id, nodes[3].node_id], &*nodes[3].entropy_source, &secp_ctx).unwrap();
-       let path = OnionMessagePath {
-               intermediate_nodes: vec![],
-               destination: Destination::BlindedPath(blinded_path),
-               first_node_addresses: None,
-       };
-       let reply_path = BlindedPath::new_for_message(&[nodes[2].node_id, nodes[1].node_id, nodes[0].node_id], &*nodes[0].entropy_source, &secp_ctx).unwrap();
-
-       nodes[0].messenger.send_onion_message_using_path(path, test_msg, Some(reply_path)).unwrap();
-       nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request);
+       let intermediate_nodes = [
+               ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
+               ForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
+       ];
+       let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[3].node_id, &*nodes[3].entropy_source, &secp_ctx).unwrap();
+       let destination = Destination::BlindedPath(blinded_path);
+       let intermediate_nodes = [
+               ForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
+               ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
+       ];
+       let reply_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[0].node_id, &*nodes[0].entropy_source, &secp_ctx).unwrap();
+
+       nodes[0].messenger.send_onion_message(test_msg, destination, Some(reply_path)).unwrap();
+       nodes[3].custom_message_handler.expect_message(TestCustomMessage::Ping);
        pass_along_path(&nodes);
 
        // Make sure the last node successfully decoded the reply path.
-       nodes[0].custom_message_handler.expect_message(TestCustomMessage::Response);
+       nodes[0].custom_message_handler.expect_message(TestCustomMessage::Pong);
        nodes.reverse();
        pass_along_path(&nodes);
 }
@@ -432,6 +624,9 @@ fn invalid_custom_message_type() {
                        // Onion message contents must have a TLV >= 64.
                        63
                }
+               fn msg_type(&self) -> &'static str {
+                       "Invalid Message"
+               }
        }
 
        impl Writeable for InvalidCustomMessage {
@@ -439,28 +634,20 @@ fn invalid_custom_message_type() {
        }
 
        let test_msg = InvalidCustomMessage {};
-       let path = OnionMessagePath {
-               intermediate_nodes: vec![],
-               destination: Destination::Node(nodes[1].node_id),
-               first_node_addresses: None,
-       };
-       let err = nodes[0].messenger.send_onion_message_using_path(path, test_msg, None).unwrap_err();
+       let destination = Destination::Node(nodes[1].node_id);
+       let err = nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap_err();
        assert_eq!(err, SendError::InvalidMessage);
 }
 
 #[test]
 fn peer_buffer_full() {
        let nodes = create_nodes(2);
-       let test_msg = TestCustomMessage::Request;
-       let path = OnionMessagePath {
-               intermediate_nodes: vec![],
-               destination: Destination::Node(nodes[1].node_id),
-               first_node_addresses: None,
-       };
+       let test_msg = TestCustomMessage::Ping;
+       let destination = Destination::Node(nodes[1].node_id);
        for _ in 0..188 { // Based on MAX_PER_PEER_BUFFER_SIZE in OnionMessenger
-               nodes[0].messenger.send_onion_message_using_path(path.clone(), test_msg.clone(), None).unwrap();
+               nodes[0].messenger.send_onion_message(test_msg.clone(), destination.clone(), None).unwrap();
        }
-       let err = nodes[0].messenger.send_onion_message_using_path(path, test_msg, None).unwrap_err();
+       let err = nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap_err();
        assert_eq!(err, SendError::BufferFull);
 }
 
@@ -470,7 +657,7 @@ fn many_hops() {
        // 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 = TestCustomMessage::Response;
+       let test_msg = TestCustomMessage::Pong;
 
        let mut intermediate_nodes = vec![];
        for i in 1..(num_nodes-1) {
@@ -483,17 +670,20 @@ fn many_hops() {
                first_node_addresses: None,
        };
        nodes[0].messenger.send_onion_message_using_path(path, test_msg, None).unwrap();
-       nodes[num_nodes-1].custom_message_handler.expect_message(TestCustomMessage::Response);
+       nodes[num_nodes-1].custom_message_handler.expect_message(TestCustomMessage::Pong);
        pass_along_path(&nodes);
 }
 
 #[test]
 fn requests_peer_connection_for_buffered_messages() {
        let nodes = create_nodes(3);
-       let message = TestCustomMessage::Request;
+       let message = TestCustomMessage::Ping;
        let secp_ctx = Secp256k1::new();
+       add_channel_to_graph(&nodes[0], &nodes[1], &secp_ctx, 42);
+
+       let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
        let blinded_path = BlindedPath::new_for_message(
-               &[nodes[1].node_id, nodes[2].node_id], &*nodes[0].entropy_source, &secp_ctx
+               &intermediate_nodes, nodes[2].node_id, &*nodes[0].entropy_source, &secp_ctx
        ).unwrap();
        let destination = Destination::BlindedPath(blinded_path);
 
@@ -525,10 +715,13 @@ fn requests_peer_connection_for_buffered_messages() {
 #[test]
 fn drops_buffered_messages_waiting_for_peer_connection() {
        let nodes = create_nodes(3);
-       let message = TestCustomMessage::Request;
+       let message = TestCustomMessage::Ping;
        let secp_ctx = Secp256k1::new();
+       add_channel_to_graph(&nodes[0], &nodes[1], &secp_ctx, 42);
+
+       let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
        let blinded_path = BlindedPath::new_for_message(
-               &[nodes[1].node_id, nodes[2].node_id], &*nodes[0].entropy_source, &secp_ctx
+               &intermediate_nodes, nodes[2].node_id, &*nodes[0].entropy_source, &secp_ctx
        ).unwrap();
        let destination = Destination::BlindedPath(blinded_path);
 
@@ -553,18 +746,84 @@ fn drops_buffered_messages_waiting_for_peer_connection() {
        assert!(nodes[0].messenger.next_onion_message_for_peer(nodes[1].node_id).is_none());
 }
 
+#[test]
+fn intercept_offline_peer_oms() {
+       // Ensure that if OnionMessenger is initialized with
+       // new_with_offline_peer_interception, we will intercept OMs for offline
+       // peers, generate the right events, and forward OMs when they are re-injected
+       // by the user.
+       let node_cfgs = vec![MessengerCfg::new(), MessengerCfg::new().with_offline_peer_interception(), MessengerCfg::new()];
+       let mut nodes = create_nodes_using_cfgs(node_cfgs);
+
+       let peer_conn_evs = release_events(&nodes[1]);
+       assert_eq!(peer_conn_evs.len(), 2);
+       for (i, ev) in peer_conn_evs.iter().enumerate() {
+               match ev {
+                       Event::OnionMessagePeerConnected { peer_node_id } => {
+                               let node_idx = if i == 0 { 0 } else { 2 };
+                               assert_eq!(peer_node_id, &nodes[node_idx].node_id);
+                       },
+                       _ => panic!()
+               }
+       }
+
+       let message = TestCustomMessage::Pong;
+       let secp_ctx = Secp256k1::new();
+       let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
+       let blinded_path = BlindedPath::new_for_message(
+               &intermediate_nodes, nodes[2].node_id, &*nodes[2].entropy_source, &secp_ctx
+       ).unwrap();
+       let destination = Destination::BlindedPath(blinded_path);
+
+       // Disconnect the peers to ensure we intercept the OM.
+       disconnect_peers(&nodes[1], &nodes[2]);
+       nodes[0].messenger.send_onion_message(message, destination, None).unwrap();
+       let mut final_node_vec = nodes.split_off(2);
+       pass_along_path(&nodes);
+
+       let mut events = release_events(&nodes[1]);
+       assert_eq!(events.len(), 1);
+       let onion_message = match events.remove(0) {
+               Event::OnionMessageIntercepted { peer_node_id, message } => {
+                       assert_eq!(peer_node_id, final_node_vec[0].node_id);
+                       message
+               },
+               _ => panic!()
+       };
+
+       // Ensure that we'll refuse to forward the re-injected OM until after the
+       // outbound peer comes back online.
+       let err = nodes[1].messenger.forward_onion_message(onion_message.clone(), &final_node_vec[0].node_id).unwrap_err();
+       assert_eq!(err, SendError::InvalidFirstHop(final_node_vec[0].node_id));
+
+       connect_peers(&nodes[1], &final_node_vec[0]);
+       let peer_conn_ev = release_events(&nodes[1]);
+       assert_eq!(peer_conn_ev.len(), 1);
+       match peer_conn_ev[0] {
+               Event::OnionMessagePeerConnected { peer_node_id } => {
+                       assert_eq!(peer_node_id, final_node_vec[0].node_id);
+               },
+               _ => panic!()
+       }
+
+       nodes[1].messenger.forward_onion_message(onion_message, &final_node_vec[0].node_id).unwrap();
+       final_node_vec[0].custom_message_handler.expect_message(TestCustomMessage::Pong);
+       pass_along_path(&vec![nodes.remove(1), final_node_vec.remove(0)]);
+}
+
 #[test]
 fn spec_test_vector() {
-       let secret_keys = [
+       let node_cfgs = [
                "4141414141414141414141414141414141414141414141414141414141414141", // Alice
                "4242424242424242424242424242424242424242424242424242424242424242", // Bob
                "4343434343434343434343434343434343434343434343434343434343434343", // Carol
                "4444444444444444444444444444444444444444444444444444444444444444", // Dave
        ]
                .iter()
-               .map(|secret| SecretKey::from_slice(&<Vec<u8>>::from_hex(secret).unwrap()).unwrap())
+               .map(|secret_hex| SecretKey::from_slice(&<Vec<u8>>::from_hex(secret_hex).unwrap()).unwrap())
+               .map(|secret| MessengerCfg::new().with_node_secret(secret))
                .collect();
-       let nodes = create_nodes_using_secrets(secret_keys);
+       let nodes = create_nodes_using_cfgs(node_cfgs);
 
        // Hardcode the sender->Alice onion message, because it includes an unknown TLV of type 1, which
        // LDK doesn't support constructing.
index 9f6c5cb435276ffbc4a76793f1f9fea624986de4..aa5f658949254fd98dd8de656c11eefba93c56d3 100644 (file)
@@ -15,15 +15,15 @@ use bitcoin::hashes::hmac::{Hmac, HmacEngine};
 use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey};
 
-use crate::blinded_path::BlindedPath;
-use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, ReceiveTlvs};
+use crate::blinded_path::{BlindedPath, IntroductionNode, NextMessageHop, NodeIdLookUp};
+use crate::blinded_path::message::{advance_path_by_one, ForwardNode, ForwardTlvs, ReceiveTlvs};
 use crate::blinded_path::utils;
 use crate::events::{Event, EventHandler, EventsProvider};
 use crate::sign::{EntropySource, NodeSigner, Recipient};
 use crate::ln::features::{InitFeatures, NodeFeatures};
 use crate::ln::msgs::{self, OnionMessage, OnionMessageHandler, SocketAddress};
 use crate::ln::onion_utils;
-use crate::routing::gossip::{NetworkGraph, NodeId};
+use crate::routing::gossip::{NetworkGraph, NodeId, ReadOnlyNetworkGraph};
 use super::packet::OnionMessageContents;
 use super::packet::ParsedOnionMessageContents;
 use super::offers::OffersMessageHandler;
@@ -70,7 +70,8 @@ pub(super) const MAX_TIMER_TICKS: usize = 2;
 /// # use bitcoin::hashes::_export::_core::time::Duration;
 /// # use bitcoin::hashes::hex::FromHex;
 /// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey, self};
-/// # use lightning::blinded_path::BlindedPath;
+/// # use lightning::blinded_path::{BlindedPath, EmptyNodeIdLookUp};
+/// # use lightning::blinded_path::message::ForwardNode;
 /// # use lightning::sign::{EntropySource, KeysManager};
 /// # use lightning::ln::peer_handler::IgnoringMessageHandler;
 /// # use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath, OnionMessenger};
@@ -97,7 +98,7 @@ pub(super) const MAX_TIMER_TICKS: usize = 2;
 /// #         })
 /// #     }
 /// #     fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
-/// #         &self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>
+/// #         &self, _recipient: PublicKey, _peers: Vec<ForwardNode>, _secp_ctx: &Secp256k1<T>
 /// #     ) -> Result<Vec<BlindedPath>, ()> {
 /// #         unreachable!()
 /// #     }
@@ -111,14 +112,15 @@ pub(super) const MAX_TIMER_TICKS: usize = 2;
 /// # let hop_node_id1 = PublicKey::from_secret_key(&secp_ctx, &node_secret);
 /// # let (hop_node_id3, hop_node_id4) = (hop_node_id1, hop_node_id1);
 /// # let destination_node_id = hop_node_id1;
+/// # let node_id_lookup = EmptyNodeIdLookUp {};
 /// # let message_router = Arc::new(FakeMessageRouter {});
 /// # let custom_message_handler = IgnoringMessageHandler {};
 /// # let offers_message_handler = IgnoringMessageHandler {};
 /// // Create the onion messenger. This must use the same `keys_manager` as is passed to your
 /// // ChannelManager.
 /// let onion_messenger = OnionMessenger::new(
-///     &keys_manager, &keys_manager, logger, message_router, &offers_message_handler,
-///     &custom_message_handler
+///     &keys_manager, &keys_manager, logger, &node_id_lookup, message_router,
+///     &offers_message_handler, &custom_message_handler
 /// );
 
 /// # #[derive(Debug)]
@@ -134,6 +136,7 @@ pub(super) const MAX_TIMER_TICKS: usize = 2;
 ///            # let your_custom_message_type = 42;
 ///            your_custom_message_type
 ///    }
+///    fn msg_type(&self) -> &'static str { "YourCustomMessageType" }
 /// }
 /// // Send a custom onion message to a node id.
 /// let destination = Destination::Node(destination_node_id);
@@ -143,8 +146,11 @@ pub(super) const MAX_TIMER_TICKS: usize = 2;
 ///
 /// // Create a blinded path to yourself, for someone to send an onion message to.
 /// # let your_node_id = hop_node_id1;
-/// let hops = [hop_node_id3, hop_node_id4, your_node_id];
-/// let blinded_path = BlindedPath::new_for_message(&hops, &keys_manager, &secp_ctx).unwrap();
+/// let hops = [
+///    ForwardNode { node_id: hop_node_id3, short_channel_id: None },
+///    ForwardNode { node_id: hop_node_id4, short_channel_id: None },
+/// ];
+/// let blinded_path = BlindedPath::new_for_message(&hops, your_node_id, &keys_manager, &secp_ctx).unwrap();
 ///
 /// // Send a custom onion message to a blinded path.
 /// let destination = Destination::BlindedPath(blinded_path);
@@ -155,11 +161,12 @@ pub(super) const MAX_TIMER_TICKS: usize = 2;
 ///
 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
-pub struct OnionMessenger<ES: Deref, NS: Deref, L: Deref, MR: Deref, OMH: Deref, CMH: Deref>
+pub struct OnionMessenger<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, CMH: Deref>
 where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
        L::Target: Logger,
+       NL::Target: NodeIdLookUp,
        MR::Target: MessageRouter,
        OMH::Target: OffersMessageHandler,
        CMH::Target: CustomOnionMessageHandler,
@@ -169,9 +176,12 @@ where
        logger: L,
        message_recipients: Mutex<HashMap<PublicKey, OnionMessageRecipient>>,
        secp_ctx: Secp256k1<secp256k1::All>,
+       node_id_lookup: NL,
        message_router: MR,
        offers_handler: OMH,
        custom_handler: CMH,
+       intercept_messages_for_offline_peers: bool,
+       pending_events: Mutex<Vec<Event>>,
 }
 
 /// [`OnionMessage`]s buffered to be sent.
@@ -243,6 +253,66 @@ impl OnionMessageRecipient {
        }
 }
 
+
+/// The `Responder` struct creates an appropriate [`ResponseInstruction`]
+/// for responding to a message.
+pub struct Responder {
+       /// The path along which a response can be sent.
+       reply_path: BlindedPath,
+       path_id: Option<[u8; 32]>
+}
+
+impl Responder {
+       /// Creates a new [`Responder`] instance with the provided reply path.
+       pub(super) fn new(reply_path: BlindedPath, path_id: Option<[u8; 32]>) -> Self {
+               Responder {
+                       reply_path,
+                       path_id,
+               }
+       }
+
+       /// Creates a [`ResponseInstruction::WithoutReplyPath`] for a given response.
+       ///
+       /// Use when the recipient doesn't need to send back a reply to us.
+       pub fn respond<T: OnionMessageContents>(self, response: T) -> ResponseInstruction<T> {
+               ResponseInstruction::WithoutReplyPath(OnionMessageResponse {
+                       message: response,
+                       reply_path: self.reply_path,
+                       path_id: self.path_id,
+               })
+       }
+
+       /// Creates a [`ResponseInstruction::WithReplyPath`] for a given response.
+       ///
+       /// Use when the recipient needs to send back a reply to us.
+       pub fn respond_with_reply_path<T: OnionMessageContents>(self, response: T) -> ResponseInstruction<T> {
+               ResponseInstruction::WithReplyPath(OnionMessageResponse {
+                       message: response,
+                       reply_path: self.reply_path,
+                       path_id: self.path_id,
+               })
+       }
+}
+
+/// This struct contains the information needed to reply to a received message.
+pub struct OnionMessageResponse<T: OnionMessageContents> {
+       message: T,
+       reply_path: BlindedPath,
+       path_id: Option<[u8; 32]>,
+}
+
+/// `ResponseInstruction` represents instructions for responding to received messages.
+pub enum ResponseInstruction<T: OnionMessageContents> {
+       /// Indicates that a response should be sent including a reply path for
+       /// the recipient to respond back.
+       WithReplyPath(OnionMessageResponse<T>),
+       /// Indicates that a response should be sent without including a reply path
+       /// for the recipient to respond back.
+       WithoutReplyPath(OnionMessageResponse<T>),
+       /// Indicates that there's no response to send back.
+       NoResponse,
+}
+
 /// An [`OnionMessage`] for [`OnionMessenger`] to send.
 ///
 /// These are obtained when released from [`OnionMessenger`]'s handlers after which they are
@@ -287,7 +357,7 @@ pub trait MessageRouter {
        fn create_blinded_paths<
                T: secp256k1::Signing + secp256k1::Verification
        >(
-               &self, recipient: PublicKey, peers: Vec<PublicKey>, secp_ctx: &Secp256k1<T>,
+               &self, recipient: PublicKey, peers: Vec<ForwardNode>, secp_ctx: &Secp256k1<T>,
        ) -> Result<Vec<BlindedPath>, ()>;
 }
 
@@ -318,15 +388,21 @@ where
        ES::Target: EntropySource,
 {
        fn find_path(
-               &self, _sender: PublicKey, peers: Vec<PublicKey>, destination: Destination
+               &self, sender: PublicKey, peers: Vec<PublicKey>, mut destination: Destination
        ) -> Result<OnionMessagePath, ()> {
-               let first_node = destination.first_node();
-               if peers.contains(&first_node) {
+               let network_graph = self.network_graph.deref().read_only();
+               destination.resolve(&network_graph);
+
+               let first_node = match destination.first_node() {
+                       Some(first_node) => first_node,
+                       None => return Err(()),
+               };
+
+               if peers.contains(&first_node) || sender == first_node {
                        Ok(OnionMessagePath {
                                intermediate_nodes: vec![], destination, first_node_addresses: None
                        })
                } else {
-                       let network_graph = self.network_graph.deref().read_only();
                        let node_announcement = network_graph
                                .node(&NodeId::from_pubkey(&first_node))
                                .and_then(|node_info| node_info.announcement_info.as_ref())
@@ -348,7 +424,7 @@ where
        fn create_blinded_paths<
                T: secp256k1::Signing + secp256k1::Verification
        >(
-               &self, recipient: PublicKey, peers: Vec<PublicKey>, secp_ctx: &Secp256k1<T>,
+               &self, recipient: PublicKey, peers: Vec<ForwardNode>, secp_ctx: &Secp256k1<T>,
        ) -> Result<Vec<BlindedPath>, ()> {
                // Limit the number of blinded paths that are computed.
                const MAX_PATHS: usize = 3;
@@ -361,13 +437,13 @@ where
                let is_recipient_announced =
                        network_graph.nodes().contains_key(&NodeId::from_pubkey(&recipient));
 
-               let mut peer_info = peers.iter()
+               let mut peer_info = peers.into_iter()
                        // Limit to peers with announced channels
-                       .filter_map(|pubkey|
+                       .filter_map(|peer|
                                network_graph
-                                       .node(&NodeId::from_pubkey(pubkey))
+                                       .node(&NodeId::from_pubkey(&peer.node_id))
                                        .filter(|info| info.channels.len() >= MIN_PEER_CHANNELS)
-                                       .map(|info| (*pubkey, info.is_tor_only(), info.channels.len()))
+                                       .map(|info| (peer, info.is_tor_only(), info.channels.len()))
                        )
                        // Exclude Tor-only nodes when the recipient is announced.
                        .filter(|(_, is_tor_only, _)| !(*is_tor_only && is_recipient_announced))
@@ -379,12 +455,13 @@ where
                });
 
                let paths = peer_info.into_iter()
-                       .map(|(pubkey, _, _)| vec![pubkey, recipient])
-                       .map(|node_pks| BlindedPath::new_for_message(&node_pks, &*self.entropy_source, secp_ctx))
+                       .map(|(peer, _, _)| {
+                               BlindedPath::new_for_message(&[peer], recipient, &*self.entropy_source, secp_ctx)
+                       })
                        .take(MAX_PATHS)
                        .collect::<Result<Vec<_>, _>>();
 
-               match paths {
+               let mut paths = match paths {
                        Ok(paths) if !paths.is_empty() => Ok(paths),
                        _ => {
                                if is_recipient_announced {
@@ -394,7 +471,12 @@ where
                                        Err(())
                                }
                        },
+               }?;
+               for path in &mut paths {
+                       path.use_compact_introduction_node(&network_graph);
                }
+
+               Ok(paths)
        }
 }
 
@@ -416,11 +498,11 @@ pub struct OnionMessagePath {
 
 impl OnionMessagePath {
        /// Returns the first node in the path.
-       pub fn first_node(&self) -> PublicKey {
+       pub fn first_node(&self) -> Option<PublicKey> {
                self.intermediate_nodes
                        .first()
                        .copied()
-                       .unwrap_or_else(|| self.destination.first_node())
+                       .or_else(|| self.destination.first_node())
        }
 }
 
@@ -434,6 +516,22 @@ pub enum Destination {
 }
 
 impl Destination {
+       /// Attempts to resolve the [`IntroductionNode::DirectedShortChannelId`] of a
+       /// [`Destination::BlindedPath`] to a [`IntroductionNode::NodeId`], if applicable, using the
+       /// provided [`ReadOnlyNetworkGraph`].
+       pub fn resolve(&mut self, network_graph: &ReadOnlyNetworkGraph) {
+               if let Destination::BlindedPath(path) = self {
+                       if let IntroductionNode::DirectedShortChannelId(..) = path.introduction_node {
+                               if let Some(pubkey) = path
+                                       .public_introduction_node_id(network_graph)
+                                       .and_then(|node_id| node_id.as_pubkey().ok())
+                               {
+                                       path.introduction_node = IntroductionNode::NodeId(pubkey);
+                               }
+                       }
+               }
+       }
+
        pub(super) fn num_hops(&self) -> usize {
                match self {
                        Destination::Node(_) => 1,
@@ -441,10 +539,15 @@ impl Destination {
                }
        }
 
-       fn first_node(&self) -> PublicKey {
+       fn first_node(&self) -> Option<PublicKey> {
                match self {
-                       Destination::Node(node_id) => *node_id,
-                       Destination::BlindedPath(BlindedPath { introduction_node_id: node_id, .. }) => *node_id,
+                       Destination::Node(node_id) => Some(*node_id),
+                       Destination::BlindedPath(BlindedPath { introduction_node, .. }) => {
+                               match introduction_node {
+                                       IntroductionNode::NodeId(pubkey) => Some(*pubkey),
+                                       IntroductionNode::DirectedShortChannelId(..) => None,
+                               }
+                       },
                }
        }
 }
@@ -477,7 +580,11 @@ pub enum SendError {
        TooFewBlindedHops,
        /// The first hop is not a peer and doesn't have a known [`SocketAddress`].
        InvalidFirstHop(PublicKey),
-       /// A path from the sender to the destination could not be found by the [`MessageRouter`].
+       /// Indicates that a path could not be found by the [`MessageRouter`].
+       ///
+       /// This occurs when either:
+       /// - No path from the sender to the destination was found to send the onion message
+       /// - No reply path to the sender could be created when responding to an onion message
        PathNotFound,
        /// Onion message contents must have a TLV type >= 64.
        InvalidMessage,
@@ -487,6 +594,10 @@ pub enum SendError {
        ///
        /// [`NodeSigner`]: crate::sign::NodeSigner
        GetNodeIdFailed,
+       /// The provided [`Destination`] has a blinded path with an unresolved introduction node. An
+       /// attempt to resolve it in the [`MessageRouter`] when finding an [`OnionMessagePath`] likely
+       /// failed.
+       UnresolvedIntroductionNode,
        /// 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
        /// [`NodeSigner::ecdh`] failed, we failed to tweak the current blinding point to get the
@@ -512,7 +623,7 @@ pub trait CustomOnionMessageHandler {
        /// Called with the custom message that was received, returning a response to send, if any.
        ///
        /// The returned [`Self::CustomMessage`], if any, is enqueued to be sent by [`OnionMessenger`].
-       fn handle_custom_message(&self, msg: Self::CustomMessage) -> Option<Self::CustomMessage>;
+       fn handle_custom_message(&self, message: Self::CustomMessage, responder: Option<Responder>) -> ResponseInstruction<Self::CustomMessage>;
 
        /// Read a custom message of type `message_type` from `buffer`, returning `Ok(None)` if the
        /// message type is unknown.
@@ -535,26 +646,59 @@ pub trait CustomOnionMessageHandler {
 
 /// A processed incoming onion message, containing either a Forward (another onion message)
 /// or a Receive payload with decrypted contents.
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 pub enum PeeledOnion<T: OnionMessageContents> {
        /// Forwarded onion, with the next node id and a new onion
-       Forward(PublicKey, OnionMessage),
+       Forward(NextMessageHop, OnionMessage),
        /// Received onion message, with decrypted contents, path_id, and reply path
        Receive(ParsedOnionMessageContents<T>, Option<[u8; 32]>, Option<BlindedPath>)
 }
 
+
+/// Creates an [`OnionMessage`] with the given `contents` for sending to the destination of
+/// `path`, first calling [`Destination::resolve`] on `path.destination` with the given
+/// [`ReadOnlyNetworkGraph`].
+///
+/// Returns the node id of the peer to send the message to, the message itself, and any addresses
+/// needed to connect to the first node.
+pub fn create_onion_message_resolving_destination<
+       ES: Deref, NS: Deref, NL: Deref, T: OnionMessageContents
+>(
+       entropy_source: &ES, node_signer: &NS, node_id_lookup: &NL,
+       network_graph: &ReadOnlyNetworkGraph, secp_ctx: &Secp256k1<secp256k1::All>,
+       mut path: OnionMessagePath, contents: T, reply_path: Option<BlindedPath>,
+) -> Result<(PublicKey, OnionMessage, Option<Vec<SocketAddress>>), SendError>
+where
+       ES::Target: EntropySource,
+       NS::Target: NodeSigner,
+       NL::Target: NodeIdLookUp,
+{
+       path.destination.resolve(network_graph);
+       create_onion_message(
+               entropy_source, node_signer, node_id_lookup, secp_ctx, path, contents, reply_path,
+       )
+}
+
 /// Creates an [`OnionMessage`] with the given `contents` for sending to the destination of
 /// `path`.
 ///
 /// Returns the node id of the peer to send the message to, the message itself, and any addresses
-/// need to connect to the first node.
-pub fn create_onion_message<ES: Deref, NS: Deref, T: OnionMessageContents>(
-       entropy_source: &ES, node_signer: &NS, secp_ctx: &Secp256k1<secp256k1::All>,
-       path: OnionMessagePath, contents: T, reply_path: Option<BlindedPath>,
+/// needed to connect to the first node.
+///
+/// Returns [`SendError::UnresolvedIntroductionNode`] if:
+/// - `destination` contains a blinded path with an [`IntroductionNode::DirectedShortChannelId`],
+/// - unless it can be resolved by [`NodeIdLookUp::next_node_id`].
+/// Use [`create_onion_message_resolving_destination`] instead to resolve the introduction node
+/// first with a [`ReadOnlyNetworkGraph`].
+pub fn create_onion_message<ES: Deref, NS: Deref, NL: Deref, T: OnionMessageContents>(
+       entropy_source: &ES, node_signer: &NS, node_id_lookup: &NL,
+       secp_ctx: &Secp256k1<secp256k1::All>, path: OnionMessagePath, contents: T,
+       reply_path: Option<BlindedPath>,
 ) -> Result<(PublicKey, OnionMessage, Option<Vec<SocketAddress>>), SendError>
 where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
+       NL::Target: NodeIdLookUp,
 {
        let OnionMessagePath { intermediate_nodes, mut destination, first_node_addresses } = path;
        if let Destination::BlindedPath(BlindedPath { ref blinded_hops, .. }) = destination {
@@ -571,8 +715,17 @@ where
                if let Destination::BlindedPath(ref mut blinded_path) = destination {
                        let our_node_id = node_signer.get_node_id(Recipient::Node)
                                .map_err(|()| SendError::GetNodeIdFailed)?;
-                       if blinded_path.introduction_node_id == our_node_id {
-                               advance_path_by_one(blinded_path, node_signer, &secp_ctx)
+                       let introduction_node_id = match blinded_path.introduction_node {
+                               IntroductionNode::NodeId(pubkey) => pubkey,
+                               IntroductionNode::DirectedShortChannelId(direction, scid) => {
+                                       match node_id_lookup.next_node_id(scid) {
+                                               Some(next_node_id) => *direction.select_pubkey(&our_node_id, &next_node_id),
+                                               None => return Err(SendError::UnresolvedIntroductionNode),
+                                       }
+                               },
+                       };
+                       if introduction_node_id == our_node_id {
+                               advance_path_by_one(blinded_path, node_signer, node_id_lookup, &secp_ctx)
                                        .map_err(|()| SendError::BlindedPathAdvanceFailed)?;
                        }
                }
@@ -583,15 +736,21 @@ where
        let (first_node_id, blinding_point) = if let Some(first_node_id) = intermediate_nodes.first() {
                (*first_node_id, PublicKey::from_secret_key(&secp_ctx, &blinding_secret))
        } else {
-               match destination {
-                       Destination::Node(pk) => (pk, PublicKey::from_secret_key(&secp_ctx, &blinding_secret)),
-                       Destination::BlindedPath(BlindedPath { introduction_node_id, blinding_point, .. }) =>
-                               (introduction_node_id, blinding_point),
+               match &destination {
+                       Destination::Node(pk) => (*pk, PublicKey::from_secret_key(&secp_ctx, &blinding_secret)),
+                       Destination::BlindedPath(BlindedPath { introduction_node, blinding_point, .. }) => {
+                               match introduction_node {
+                                       IntroductionNode::NodeId(pubkey) => (*pubkey, *blinding_point),
+                                       IntroductionNode::DirectedShortChannelId(..) => {
+                                               return Err(SendError::UnresolvedIntroductionNode);
+                                       },
+                               }
+                       }
                }
        };
        let (packet_payloads, packet_keys) = packet_payloads_and_keys(
-               &secp_ctx, &intermediate_nodes, destination, contents, reply_path, &blinding_secret)
-               .map_err(|e| SendError::Secp256k1(e))?;
+               &secp_ctx, &intermediate_nodes, destination, contents, reply_path, &blinding_secret
+       )?;
 
        let prng_seed = entropy_source.get_secure_random_bytes();
        let onion_routing_packet = construct_onion_message_packet(
@@ -647,9 +806,9 @@ where
                        Ok(PeeledOnion::Receive(message, path_id, reply_path))
                },
                Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
-                       next_node_id, next_blinding_override
+                       next_hop, next_blinding_override
                })), Some((next_hop_hmac, new_packet_bytes)))) => {
-                       // TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
+                       // TODO: we need to check whether `next_hop` is our node, in which case this is a dummy
                        // blinded hop and this onion message is destined for us. In this situation, we should keep
                        // unwrapping the onion layers to get to the final payload. Since we don't have the option
                        // of creating blinded paths with dummy hops currently, we should be ok to not handle this
@@ -685,7 +844,7 @@ where
                                onion_routing_packet: outgoing_packet,
                        };
 
-                       Ok(PeeledOnion::Forward(next_node_id, onion_message))
+                       Ok(PeeledOnion::Forward(next_hop, onion_message))
                },
                Err(e) => {
                        log_trace!(logger, "Errored decoding onion message packet: {:?}", e);
@@ -698,12 +857,13 @@ where
        }
 }
 
-impl<ES: Deref, NS: Deref, L: Deref, MR: Deref, OMH: Deref, CMH: Deref>
-OnionMessenger<ES, NS, L, MR, OMH, CMH>
+impl<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, CMH: Deref>
+OnionMessenger<ES, NS, L, NL, MR, OMH, CMH>
 where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
        L::Target: Logger,
+       NL::Target: NodeIdLookUp,
        MR::Target: MessageRouter,
        OMH::Target: OffersMessageHandler,
        CMH::Target: CustomOnionMessageHandler,
@@ -711,8 +871,50 @@ where
        /// Constructs a new `OnionMessenger` to send, forward, and delegate received onion messages to
        /// their respective handlers.
        pub fn new(
-               entropy_source: ES, node_signer: NS, logger: L, message_router: MR, offers_handler: OMH,
-               custom_handler: CMH
+               entropy_source: ES, node_signer: NS, logger: L, node_id_lookup: NL, message_router: MR,
+               offers_handler: OMH, custom_handler: CMH
+       ) -> Self {
+               Self::new_inner(
+                       entropy_source, node_signer, logger, node_id_lookup, message_router,
+                       offers_handler, custom_handler, false
+               )
+       }
+
+       /// Similar to [`Self::new`], but rather than dropping onion messages that are
+       /// intended to be forwarded to offline peers, we will intercept them for
+       /// later forwarding.
+       ///
+       /// Interception flow:
+       /// 1. If an onion message for an offline peer is received, `OnionMessenger` will
+       ///    generate an [`Event::OnionMessageIntercepted`]. Event handlers can
+       ///    then choose to persist this onion message for later forwarding, or drop
+       ///    it.
+       /// 2. When the offline peer later comes back online, `OnionMessenger` will
+       ///    generate an [`Event::OnionMessagePeerConnected`]. Event handlers will
+       ///    then fetch all previously intercepted onion messages for this peer.
+       /// 3. Once the stored onion messages are fetched, they can finally be
+       ///    forwarded to the now-online peer via [`Self::forward_onion_message`].
+       ///
+       /// # Note
+       ///
+       /// LDK will not rate limit how many [`Event::OnionMessageIntercepted`]s
+       /// are generated, so it is the caller's responsibility to limit how many
+       /// onion messages are persisted and only persist onion messages for relevant
+       /// peers.
+       pub fn new_with_offline_peer_interception(
+               entropy_source: ES, node_signer: NS, logger: L, node_id_lookup: NL,
+               message_router: MR, offers_handler: OMH, custom_handler: CMH
+       ) -> Self {
+               Self::new_inner(
+                       entropy_source, node_signer, logger, node_id_lookup, message_router,
+                       offers_handler, custom_handler, true
+               )
+       }
+
+       fn new_inner(
+               entropy_source: ES, node_signer: NS, logger: L, node_id_lookup: NL,
+               message_router: MR, offers_handler: OMH, custom_handler: CMH,
+               intercept_messages_for_offline_peers: bool
        ) -> Self {
                let mut secp_ctx = Secp256k1::new();
                secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
@@ -722,9 +924,12 @@ where
                        message_recipients: Mutex::new(new_hash_map()),
                        secp_ctx,
                        logger,
+                       node_id_lookup,
                        message_router,
                        offers_handler,
                        custom_handler,
+                       intercept_messages_for_offline_peers,
+                       pending_events: Mutex::new(Vec::new()),
                }
        }
 
@@ -748,13 +953,12 @@ where
                &self, contents: T, destination: Destination, reply_path: Option<BlindedPath>,
                log_suffix: fmt::Arguments
        ) -> Result<SendSuccess, SendError> {
-               let mut logger = WithContext::from(&self.logger, None, None);
-               let result = self.find_path(destination)
-                       .and_then(|path| {
-                               let first_hop = path.intermediate_nodes.get(0).map(|p| *p);
-                               logger = WithContext::from(&self.logger, first_hop, None);
-                               self.enqueue_onion_message(path, contents, reply_path, log_suffix)
-                       });
+               let mut logger = WithContext::from(&self.logger, None, None, None);
+               let result = self.find_path(destination).and_then(|path| {
+                       let first_hop = path.intermediate_nodes.get(0).map(|p| *p);
+                       logger = WithContext::from(&self.logger, first_hop, None, None);
+                       self.enqueue_onion_message(path, contents, reply_path, log_suffix)
+               });
 
                match result.as_ref() {
                        Err(SendError::GetNodeIdFailed) => {
@@ -797,6 +1001,27 @@ where
                        .map_err(|_| SendError::PathNotFound)
        }
 
+       fn create_blinded_path(&self) -> Result<BlindedPath, SendError> {
+               let recipient = self.node_signer
+                       .get_node_id(Recipient::Node)
+                       .map_err(|_| SendError::GetNodeIdFailed)?;
+               let secp_ctx = &self.secp_ctx;
+
+               let peers = self.message_recipients.lock().unwrap()
+                       .iter()
+                       .filter(|(_, peer)| matches!(peer, OnionMessageRecipient::ConnectedPeer(_)))
+                       .map(|(node_id, _ )| ForwardNode {
+                               node_id: *node_id,
+                               short_channel_id: None,
+                       })
+                       .collect::<Vec<_>>();
+
+               self.message_router
+                       .create_blinded_paths(recipient, peers, secp_ctx)
+                       .and_then(|paths| paths.into_iter().next().ok_or(()))
+                       .map_err(|_| SendError::PathNotFound)
+       }
+
        fn enqueue_onion_message<T: OnionMessageContents>(
                &self, path: OnionMessagePath, contents: T, reply_path: Option<BlindedPath>,
                log_suffix: fmt::Arguments
@@ -804,7 +1029,8 @@ where
                log_trace!(self.logger, "Constructing onion message {}: {:?}", log_suffix, contents);
 
                let (first_node_id, onion_message, addresses) = create_onion_message(
-                       &self.entropy_source, &self.node_signer, &self.secp_ctx, path, contents, reply_path
+                       &self.entropy_source, &self.node_signer, &self.node_id_lookup, &self.secp_ctx, path,
+                       contents, reply_path,
                )?;
 
                let mut message_recipients = self.message_recipients.lock().unwrap();
@@ -832,6 +1058,27 @@ where
                }
        }
 
+       /// Forwards an [`OnionMessage`] to `peer_node_id`. Useful if we initialized
+       /// the [`OnionMessenger`] with [`Self::new_with_offline_peer_interception`]
+       /// and want to forward a previously intercepted onion message to a peer that
+       /// has just come online.
+       pub fn forward_onion_message(
+               &self, message: OnionMessage, peer_node_id: &PublicKey
+       ) -> Result<(), SendError> {
+               let mut message_recipients = self.message_recipients.lock().unwrap();
+               if outbound_buffer_full(&peer_node_id, &message_recipients) {
+                       return Err(SendError::BufferFull);
+               }
+
+               match message_recipients.entry(*peer_node_id) {
+                       hash_map::Entry::Occupied(mut e) if e.get().is_connected() => {
+                               e.get_mut().enqueue_message(message);
+                               Ok(())
+                       },
+                       _ => Err(SendError::InvalidFirstHop(*peer_node_id))
+               }
+       }
+
        #[cfg(any(test, feature = "_test_utils"))]
        pub fn send_onion_message_using_path<T: OnionMessageContents>(
                &self, path: OnionMessagePath, contents: T, reply_path: Option<BlindedPath>
@@ -847,21 +1094,48 @@ where
                )
        }
 
-       fn handle_onion_message_response<T: OnionMessageContents>(
-               &self, response: Option<T>, reply_path: Option<BlindedPath>, log_suffix: fmt::Arguments
-       ) {
-               if let Some(response) = response {
-                       match reply_path {
-                               Some(reply_path) => {
-                                       let _ = self.find_path_and_enqueue_onion_message(
-                                               response, Destination::BlindedPath(reply_path), None, log_suffix
+       /// Handles the response to an [`OnionMessage`] based on its [`ResponseInstruction`],
+       /// enqueueing any response for sending.
+       ///
+       /// This function is useful for asynchronous handling of [`OnionMessage`]s.
+       /// Handlers have the option to return [`ResponseInstruction::NoResponse`], indicating that
+       /// no immediate response should be sent. Then, they can transfer the associated [`Responder`]
+       /// to another task responsible for generating the response asynchronously. Subsequently, when
+       /// the response is prepared and ready for sending, that task can invoke this method to enqueue
+       /// the response for delivery.
+       pub fn handle_onion_message_response<T: OnionMessageContents>(
+               &self, response: ResponseInstruction<T>
+       ) -> Result<Option<SendSuccess>, SendError> {
+               let (response, create_reply_path) = match response {
+                       ResponseInstruction::WithReplyPath(response) => (response, true),
+                       ResponseInstruction::WithoutReplyPath(response) => (response, false),
+                       ResponseInstruction::NoResponse => return Ok(None),
+               };
+
+               let message_type = response.message.msg_type();
+               let reply_path = if create_reply_path {
+                       match self.create_blinded_path() {
+                               Ok(reply_path) => Some(reply_path),
+                               Err(err) => {
+                                       log_trace!(
+                                               self.logger,
+                                               "Failed to create reply path when responding with {} to an onion message \
+                                               with path_id {:02x?}: {:?}",
+                                               message_type, response.path_id, err
                                        );
-                               },
-                               None => {
-                                       log_trace!(self.logger, "Missing reply path {}", log_suffix);
-                               },
+                                       return Err(err);
+                               }
                        }
-               }
+               } else { None };
+
+               self.find_path_and_enqueue_onion_message(
+                       response.message, Destination::BlindedPath(response.reply_path), reply_path,
+                       format_args!(
+                               "when responding with {} to an onion message with path_id {:02x?}",
+                               message_type,
+                               response.path_id
+                       )
+               ).map(|result| Some(result))
        }
 
        #[cfg(test)]
@@ -875,6 +1149,20 @@ where
                }
                msgs
        }
+
+       fn enqueue_event(&self, event: Event) {
+               const MAX_EVENTS_BUFFER_SIZE: usize = (1 << 10) * 256;
+               let mut pending_events = self.pending_events.lock().unwrap();
+               let total_buffered_bytes: usize = pending_events
+                       .iter()
+                       .map(|ev| ev.serialized_length())
+                       .sum();
+               if total_buffered_bytes >= MAX_EVENTS_BUFFER_SIZE {
+                       log_trace!(self.logger, "Dropping event {:?}: buffer full", event);
+                       return
+               }
+               pending_events.push(event);
+       }
 }
 
 fn outbound_buffer_full(peer_node_id: &PublicKey, buffer: &HashMap<PublicKey, OnionMessageRecipient>) -> bool {
@@ -900,12 +1188,13 @@ fn outbound_buffer_full(peer_node_id: &PublicKey, buffer: &HashMap<PublicKey, On
        false
 }
 
-impl<ES: Deref, NS: Deref, L: Deref, MR: Deref, OMH: Deref, CMH: Deref> EventsProvider
-for OnionMessenger<ES, NS, L, MR, OMH, CMH>
+impl<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, CMH: Deref> EventsProvider
+for OnionMessenger<ES, NS, L, NL, MR, OMH, CMH>
 where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
        L::Target: Logger,
+       NL::Target: NodeIdLookUp,
        MR::Target: MessageRouter,
        OMH::Target: OffersMessageHandler,
        CMH::Target: CustomOnionMessageHandler,
@@ -918,21 +1207,27 @@ where
                                }
                        }
                }
+               let mut events = Vec::new();
+               core::mem::swap(&mut *self.pending_events.lock().unwrap(), &mut events);
+               for ev in events {
+                       handler.handle_event(ev);
+               }
        }
 }
 
-impl<ES: Deref, NS: Deref, L: Deref, MR: Deref, OMH: Deref, CMH: Deref> OnionMessageHandler
-for OnionMessenger<ES, NS, L, MR, OMH, CMH>
+impl<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, CMH: Deref> OnionMessageHandler
+for OnionMessenger<ES, NS, L, NL, MR, OMH, CMH>
 where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
        L::Target: Logger,
+       NL::Target: NodeIdLookUp,
        MR::Target: MessageRouter,
        OMH::Target: OffersMessageHandler,
        CMH::Target: CustomOnionMessageHandler,
 {
        fn handle_onion_message(&self, peer_node_id: &PublicKey, msg: &OnionMessage) {
-               let logger = WithContext::from(&self.logger, Some(*peer_node_id), None);
+               let logger = WithContext::from(&self.logger, Some(*peer_node_id), None, None);
                match self.peel_onion_message(msg) {
                        Ok(PeeledOnion::Receive(message, path_id, reply_path)) => {
                                log_trace!(
@@ -942,26 +1237,33 @@ where
 
                                match message {
                                        ParsedOnionMessageContents::Offers(msg) => {
-                                               let response = self.offers_handler.handle_message(msg);
-                                               self.handle_onion_message_response(
-                                                       response, reply_path, format_args!(
-                                                               "when responding to Offers onion message with path_id {:02x?}",
-                                                               path_id
-                                                       )
+                                               let responder = reply_path.map(
+                                                       |reply_path| Responder::new(reply_path, path_id)
                                                );
+                                               let response_instructions = self.offers_handler.handle_message(msg, responder);
+                                               let _ = self.handle_onion_message_response(response_instructions);
                                        },
                                        ParsedOnionMessageContents::Custom(msg) => {
-                                               let response = self.custom_handler.handle_custom_message(msg);
-                                               self.handle_onion_message_response(
-                                                       response, reply_path, format_args!(
-                                                               "when responding to Custom onion message with path_id {:02x?}",
-                                                               path_id
-                                                       )
+                                               let responder = reply_path.map(
+                                                       |reply_path| Responder::new(reply_path, path_id)
                                                );
+                                               let response_instructions = self.custom_handler.handle_custom_message(msg, responder);
+                                               let _ = self.handle_onion_message_response(response_instructions);
                                        },
                                }
                        },
-                       Ok(PeeledOnion::Forward(next_node_id, onion_message)) => {
+                       Ok(PeeledOnion::Forward(next_hop, onion_message)) => {
+                               let next_node_id = match next_hop {
+                                       NextMessageHop::NodeId(pubkey) => pubkey,
+                                       NextMessageHop::ShortChannelId(scid) => match self.node_id_lookup.next_node_id(scid) {
+                                               Some(pubkey) => pubkey,
+                                               None => {
+                                                       log_trace!(self.logger, "Dropping forwarded onion messager: unable to resolve next hop using SCID {}", scid);
+                                                       return
+                                               },
+                                       },
+                               };
+
                                let mut message_recipients = self.message_recipients.lock().unwrap();
                                if outbound_buffer_full(&next_node_id, &message_recipients) {
                                        log_trace!(
@@ -983,6 +1285,13 @@ where
                                                e.get_mut().enqueue_message(onion_message);
                                                log_trace!(logger, "Forwarding an onion message to peer {}", next_node_id);
                                        },
+                                       _ if self.intercept_messages_for_offline_peers => {
+                                               self.enqueue_event(
+                                                       Event::OnionMessageIntercepted {
+                                                               peer_node_id: next_node_id, message: onion_message
+                                                       }
+                                               );
+                                       },
                                        _ => {
                                                log_trace!(
                                                        logger,
@@ -1004,6 +1313,11 @@ where
                                .entry(*their_node_id)
                                .or_insert_with(|| OnionMessageRecipient::ConnectedPeer(VecDeque::new()))
                                .mark_connected();
+                       if self.intercept_messages_for_offline_peers {
+                               self.enqueue_event(
+                                       Event::OnionMessagePeerConnected { peer_node_id: *their_node_id }
+                               );
+                       }
                } else {
                        self.message_recipients.lock().unwrap().remove(their_node_id);
                }
@@ -1097,6 +1411,7 @@ pub type SimpleArcOnionMessenger<M, T, F, L> = OnionMessenger<
        Arc<KeysManager>,
        Arc<KeysManager>,
        Arc<L>,
+       Arc<SimpleArcChannelManager<M, T, F, L>>,
        Arc<DefaultMessageRouter<Arc<NetworkGraph<Arc<L>>>, Arc<L>, Arc<KeysManager>>>,
        Arc<SimpleArcChannelManager<M, T, F, L>>,
        IgnoringMessageHandler
@@ -1116,8 +1431,9 @@ pub type SimpleRefOnionMessenger<
        &'a KeysManager,
        &'a KeysManager,
        &'b L,
-       &'i DefaultMessageRouter<&'g NetworkGraph<&'b L>, &'b L, &'a KeysManager>,
-       &'j SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L>,
+       &'i SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L>,
+       &'j DefaultMessageRouter<&'g NetworkGraph<&'b L>, &'b L, &'a KeysManager>,
+       &'i SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L>,
        IgnoringMessageHandler
 >;
 
@@ -1126,14 +1442,23 @@ pub type SimpleRefOnionMessenger<
 fn packet_payloads_and_keys<T: OnionMessageContents, S: secp256k1::Signing + secp256k1::Verification>(
        secp_ctx: &Secp256k1<S>, unblinded_path: &[PublicKey], destination: Destination, message: T,
        mut reply_path: Option<BlindedPath>, session_priv: &SecretKey
-) -> Result<(Vec<(Payload<T>, [u8; 32])>, Vec<onion_utils::OnionKeys>), secp256k1::Error> {
+) -> Result<(Vec<(Payload<T>, [u8; 32])>, Vec<onion_utils::OnionKeys>), SendError> {
        let num_hops = unblinded_path.len() + destination.num_hops();
        let mut payloads = Vec::with_capacity(num_hops);
        let mut onion_packet_keys = Vec::with_capacity(num_hops);
 
-       let (mut intro_node_id_blinding_pt, num_blinded_hops) = if let Destination::BlindedPath(BlindedPath {
-               introduction_node_id, blinding_point, blinded_hops }) = &destination {
-               (Some((*introduction_node_id, *blinding_point)), blinded_hops.len()) } else { (None, 0) };
+       let (mut intro_node_id_blinding_pt, num_blinded_hops) = match &destination {
+               Destination::Node(_) => (None, 0),
+               Destination::BlindedPath(BlindedPath { introduction_node, blinding_point, blinded_hops }) => {
+                       let introduction_node_id = match introduction_node {
+                               IntroductionNode::NodeId(pubkey) => pubkey,
+                               IntroductionNode::DirectedShortChannelId(..) => {
+                                       return Err(SendError::UnresolvedIntroductionNode);
+                               },
+                       };
+                       (Some((*introduction_node_id, *blinding_point)), blinded_hops.len())
+               },
+       };
        let num_unblinded_hops = num_hops - num_blinded_hops;
 
        let mut unblinded_path_idx = 0;
@@ -1146,7 +1471,7 @@ fn packet_payloads_and_keys<T: OnionMessageContents, S: secp256k1::Signing + sec
                                if let Some(ss) = prev_control_tlvs_ss.take() {
                                        payloads.push((Payload::Forward(ForwardControlTlvs::Unblinded(
                                                ForwardTlvs {
-                                                       next_node_id: unblinded_pk_opt.unwrap(),
+                                                       next_hop: NextMessageHop::NodeId(unblinded_pk_opt.unwrap()),
                                                        next_blinding_override: None,
                                                }
                                        )), ss));
@@ -1156,7 +1481,7 @@ fn packet_payloads_and_keys<T: OnionMessageContents, S: secp256k1::Signing + sec
                        } else if let Some((intro_node_id, blinding_pt)) = intro_node_id_blinding_pt.take() {
                                if let Some(control_tlvs_ss) = prev_control_tlvs_ss.take() {
                                        payloads.push((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
-                                               next_node_id: intro_node_id,
+                                               next_hop: NextMessageHop::NodeId(intro_node_id),
                                                next_blinding_override: Some(blinding_pt),
                                        })), control_tlvs_ss));
                                }
@@ -1181,7 +1506,7 @@ fn packet_payloads_and_keys<T: OnionMessageContents, S: secp256k1::Signing + sec
                                mu,
                        });
                }
-       )?;
+       ).map_err(|e| SendError::Secp256k1(e))?;
 
        if let Some(control_tlvs) = final_control_tlvs {
                payloads.push((Payload::Receive {
index 6672b5dfcdd0f3e33025c4bfe0654d6cfd2b0e56..42c6914157e74f345ed0d2ad354834262653646a 100644 (file)
@@ -9,7 +9,6 @@
 
 //! Message handling for BOLT 12 Offers.
 
-use core::convert::TryFrom;
 use core::fmt;
 use crate::io::{self, Read};
 use crate::ln::msgs::DecodeError;
@@ -20,6 +19,7 @@ use crate::offers::parse::Bolt12ParseError;
 use crate::onion_message::packet::OnionMessageContents;
 use crate::util::logger::Logger;
 use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
+use crate::onion_message::messenger::{ResponseInstruction, Responder};
 #[cfg(not(c_bindings))]
 use crate::onion_message::messenger::PendingOnionMessage;
 
@@ -40,7 +40,7 @@ pub trait OffersMessageHandler {
        /// The returned [`OffersMessage`], if any, is enqueued to be sent by [`OnionMessenger`].
        ///
        /// [`OnionMessenger`]: crate::onion_message::messenger::OnionMessenger
-       fn handle_message(&self, message: OffersMessage) -> Option<OffersMessage>;
+       fn handle_message(&self, message: OffersMessage, responder: Option<Responder>) -> ResponseInstruction<OffersMessage>;
 
        /// Releases any [`OffersMessage`]s that need to be sent.
        ///
@@ -118,6 +118,13 @@ impl OnionMessageContents for OffersMessage {
                        OffersMessage::InvoiceError(_) => INVOICE_ERROR_TLV_TYPE,
                }
        }
+       fn msg_type(&self) -> &'static str {
+               match &self {
+                       OffersMessage::InvoiceRequest(_) => "Invoice Request",
+                       OffersMessage::Invoice(_) => "Invoice",
+                       OffersMessage::InvoiceError(_) => "Invoice Error",
+               }
+       }
 }
 
 impl Writeable for OffersMessage {
index d9349fdadbfaba6c1f0d08c265c48bf14da49bb7..75ec3cd90ab83c811b18b1283eab668ca7ddcb1d 100644 (file)
@@ -12,7 +12,7 @@
 use bitcoin::secp256k1::PublicKey;
 use bitcoin::secp256k1::ecdh::SharedSecret;
 
-use crate::blinded_path::BlindedPath;
+use crate::blinded_path::{BlindedPath, NextMessageHop};
 use crate::blinded_path::message::{ForwardTlvs, ReceiveTlvs};
 use crate::blinded_path::utils::Padding;
 use crate::ln::msgs::DecodeError;
@@ -24,6 +24,7 @@ use crate::util::logger::Logger;
 use crate::util::ser::{BigSize, FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer};
 
 use core::cmp;
+use core::fmt;
 use crate::io::{self, Read};
 use crate::prelude::*;
 
@@ -33,7 +34,7 @@ pub(super) const SMALL_PACKET_HOP_DATA_LEN: usize = 1300;
 pub(super) const BIG_PACKET_HOP_DATA_LEN: usize = 32768;
 
 /// Packet of hop data for next peer
-#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[derive(Clone, Hash, PartialEq, Eq)]
 pub struct Packet {
        /// Bolt 04 version number
        pub version: u8,
@@ -62,6 +63,12 @@ impl onion_utils::Packet for Packet {
        }
 }
 
+impl fmt::Debug for Packet {
+       fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+               f.write_fmt(format_args!("Onion message packet version {} with hmac {:?}", self.version, &self.hmac[..]))
+       }
+}
+
 impl Writeable for Packet {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
                self.version.write(w)?;
@@ -117,7 +124,7 @@ pub(super) enum Payload<T: OnionMessageContents> {
 /// The contents of an [`OnionMessage`] as read from the wire.
 ///
 /// [`OnionMessage`]: crate::ln::msgs::OnionMessage
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 pub enum ParsedOnionMessageContents<T: OnionMessageContents> {
        /// A message related to BOLT 12 Offers.
        Offers(OffersMessage),
@@ -135,6 +142,12 @@ impl<T: OnionMessageContents> OnionMessageContents for ParsedOnionMessageContent
                        &ParsedOnionMessageContents::Custom(ref msg) => msg.tlv_type(),
                }
        }
+       fn msg_type(&self) -> &'static str {
+               match self {
+                       ParsedOnionMessageContents::Offers(ref msg) => msg.msg_type(),
+                       ParsedOnionMessageContents::Custom(ref msg) => msg.msg_type(),
+               }
+       }
 }
 
 impl<T: OnionMessageContents> Writeable for ParsedOnionMessageContents<T> {
@@ -150,6 +163,9 @@ impl<T: OnionMessageContents> Writeable for ParsedOnionMessageContents<T> {
 pub trait OnionMessageContents: Writeable + core::fmt::Debug {
        /// Returns the TLV type identifying the message contents. MUST be >= 64.
        fn tlv_type(&self) -> u64;
+
+       /// Returns the message type
+       fn msg_type(&self) -> &'static str;
 }
 
 /// Forward control TLVs in their blinded and unblinded form.
@@ -284,20 +300,26 @@ impl Readable for ControlTlvs {
        fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
                _init_and_read_tlv_stream!(r, {
                        (1, _padding, option),
-                       (2, _short_channel_id, option),
+                       (2, short_channel_id, option),
                        (4, next_node_id, option),
                        (6, path_id, option),
                        (8, next_blinding_override, option),
                });
                let _padding: Option<Padding> = _padding;
-               let _short_channel_id: Option<u64> = _short_channel_id;
 
-               let valid_fwd_fmt  = next_node_id.is_some() && path_id.is_none();
-               let valid_recv_fmt = next_node_id.is_none() && next_blinding_override.is_none();
+               let next_hop = match (short_channel_id, next_node_id) {
+                       (Some(_), Some(_)) => return Err(DecodeError::InvalidValue),
+                       (Some(scid), None) => Some(NextMessageHop::ShortChannelId(scid)),
+                       (None, Some(pubkey)) => Some(NextMessageHop::NodeId(pubkey)),
+                       (None, None) => None,
+               };
+
+               let valid_fwd_fmt = next_hop.is_some() && path_id.is_none();
+               let valid_recv_fmt = next_hop.is_none() && next_blinding_override.is_none();
 
                let payload_fmt = if valid_fwd_fmt {
                        ControlTlvs::Forward(ForwardTlvs {
-                               next_node_id: next_node_id.unwrap(),
+                               next_hop: next_hop.unwrap(),
                                next_blinding_override,
                        })
                } else if valid_recv_fmt {
index 0132a647b4c31250c55cbac49828520d32a4df29..15a6e1302b8e4d0521086f5bb21c682fb8ed2542 100644 (file)
@@ -9,6 +9,7 @@
 
 //! The [`NetworkGraph`] stores the network gossip and [`P2PGossipSync`] fetches it from peers
 
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::constants::ChainHash;
 
 use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
@@ -18,10 +19,10 @@ use bitcoin::secp256k1;
 
 use bitcoin::hashes::sha256d::Hash as Sha256dHash;
 use bitcoin::hashes::Hash;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 
 use crate::events::{MessageSendEvent, MessageSendEventsProvider};
-use crate::ln::ChannelId;
+use crate::ln::types::ChannelId;
 use crate::ln::features::{ChannelFeatures, NodeFeatures, InitFeatures};
 use crate::ln::msgs::{DecodeError, ErrorAction, Init, LightningError, RoutingMessageHandler, SocketAddress, MAX_VALUE_MSAT};
 use crate::ln::msgs::{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement, GossipTimestampFilter};
@@ -38,7 +39,6 @@ use crate::io;
 use crate::io_extras::{copy, sink};
 use crate::prelude::*;
 use core::{cmp, fmt};
-use core::convert::TryFrom;
 use crate::sync::{RwLock, RwLockReadGuard, LockTestExt};
 use core::sync::atomic::{AtomicUsize, Ordering};
 use crate::sync::Mutex;
@@ -760,22 +760,32 @@ where
        }
 }
 
+// Fetching values from this struct is very performance sensitive during routefinding. Thus, we
+// want to ensure that all of the fields we care about (all of them except `last_update_message`)
+// sit on the same cache line.
+//
+// We do this by using `repr(C)`, which forces the struct to be laid out in memory the way we write
+// it (ensuring `last_update_message` hangs off the end and no fields are reordered after it), and
+// `align(32)`, ensuring the struct starts either at the start, or in the middle, of a 64b x86-64
+// cache line. This ensures the beginning fields (which are 31 bytes) all sit in the same cache
+// line.
+#[repr(C, align(32))]
 #[derive(Clone, Debug, PartialEq, Eq)]
 /// Details about one direction of a channel as received within a [`ChannelUpdate`].
 pub struct ChannelUpdateInfo {
-       /// When the last update to the channel direction was issued.
-       /// Value is opaque, as set in the announcement.
-       pub last_update: u32,
-       /// Whether the channel can be currently used for payments (in this one direction).
-       pub enabled: bool,
-       /// The difference in CLTV values that you must have when routing through this channel.
-       pub cltv_expiry_delta: u16,
        /// The minimum value, which must be relayed to the next hop via the channel
        pub htlc_minimum_msat: u64,
        /// The maximum value which may be relayed to the next hop via the channel.
        pub htlc_maximum_msat: u64,
        /// Fees charged when the channel is used for routing
        pub fees: RoutingFees,
+       /// When the last update to the channel direction was issued.
+       /// Value is opaque, as set in the announcement.
+       pub last_update: u32,
+       /// The difference in CLTV values that you must have when routing through this channel.
+       pub cltv_expiry_delta: u16,
+       /// Whether the channel can be currently used for payments (in this one direction).
+       pub enabled: bool,
        /// Most recent update for the channel received from the network
        /// Mostly redundant with the data we store in fields explicitly.
        /// Everything else is useful only for sending out for initial routing sync.
@@ -843,22 +853,46 @@ impl Readable for ChannelUpdateInfo {
        }
 }
 
+// Fetching values from this struct is very performance sensitive during routefinding. Thus, we
+// want to ensure that all of the fields we care about (all of them except `last_update_message`
+// and `announcement_received_time`) sit on the same cache line.
+//
+// Sadly, this is not possible, however we can still do okay - all of the fields before
+// `one_to_two` and `two_to_one` are just under 128 bytes long, so we can ensure they sit on
+// adjacent cache lines (which are generally fetched together in x86_64 processors).
+//
+// This leaves only the two directional channel info structs on separate cache lines.
+//
+// We accomplish this using `repr(C)`, which forces the struct to be laid out in memory the way we
+// write it (ensuring the fields we care about are at the start of the struct) and `align(128)`,
+// ensuring the struct starts at the beginning of two adjacent 64b x86-64 cache lines.
+#[repr(align(128), C)]
 #[derive(Clone, Debug, Eq)]
 /// Details about a channel (both directions).
 /// Received within a channel announcement.
 pub struct ChannelInfo {
        /// Protocol features of a channel communicated during its announcement
        pub features: ChannelFeatures,
+
        /// Source node of the first direction of a channel
        pub node_one: NodeId,
-       /// Details about the first direction of a channel
-       pub one_to_two: Option<ChannelUpdateInfo>,
+
        /// Source node of the second direction of a channel
        pub node_two: NodeId,
-       /// Details about the second direction of a channel
-       pub two_to_one: Option<ChannelUpdateInfo>,
+
+       /// The [`NodeInfo::node_counter`] of the node pointed to by [`Self::node_one`].
+       pub(crate) node_one_counter: u32,
+       /// The [`NodeInfo::node_counter`] of the node pointed to by [`Self::node_two`].
+       pub(crate) node_two_counter: u32,
+
        /// The channel capacity as seen on-chain, if chain lookup is available.
        pub capacity_sats: Option<u64>,
+
+       /// Details about the first direction of a channel
+       pub one_to_two: Option<ChannelUpdateInfo>,
+       /// Details about the second direction of a channel
+       pub two_to_one: Option<ChannelUpdateInfo>,
+
        /// An initial announcement of the channel
        /// Mostly redundant with the data we store in fields explicitly.
        /// Everything else is useful only for sending out for initial routing sync.
@@ -868,11 +902,6 @@ pub struct ChannelInfo {
        /// (which we can probably assume we are - no-std environments probably won't have a full
        /// network graph in memory!).
        announcement_received_time: u64,
-
-       /// The [`NodeInfo::node_counter`] of the node pointed to by [`Self::node_one`].
-       pub(crate) node_one_counter: u32,
-       /// The [`NodeInfo::node_counter`] of the node pointed to by [`Self::node_two`].
-       pub(crate) node_two_counter: u32,
 }
 
 impl PartialEq for ChannelInfo {
@@ -892,6 +921,7 @@ impl ChannelInfo {
        /// Returns a [`DirectedChannelInfo`] for the channel directed to the given `target` from a
        /// returned `source`, or `None` if `target` is not one of the channel's counterparties.
        pub fn as_directed_to(&self, target: &NodeId) -> Option<(DirectedChannelInfo, &NodeId)> {
+               if self.one_to_two.is_none() || self.two_to_one.is_none() { return None; }
                let (direction, source, outbound) = {
                        if target == &self.node_one {
                                (self.two_to_one.as_ref(), &self.node_two, false)
@@ -901,12 +931,14 @@ impl ChannelInfo {
                                return None;
                        }
                };
-               direction.map(|dir| (DirectedChannelInfo::new(self, dir, outbound), source))
+               let dir = direction.expect("We checked that both directions are available at the start");
+               Some((DirectedChannelInfo::new(self, dir, outbound), source))
        }
 
        /// Returns a [`DirectedChannelInfo`] for the channel directed from the given `source` to a
        /// returned `target`, or `None` if `source` is not one of the channel's counterparties.
        pub fn as_directed_from(&self, source: &NodeId) -> Option<(DirectedChannelInfo, &NodeId)> {
+               if self.one_to_two.is_none() || self.two_to_one.is_none() { return None; }
                let (direction, target, outbound) = {
                        if source == &self.node_one {
                                (self.one_to_two.as_ref(), &self.node_two, true)
@@ -916,7 +948,8 @@ impl ChannelInfo {
                                return None;
                        }
                };
-               direction.map(|dir| (DirectedChannelInfo::new(self, dir, outbound), target))
+               let dir = direction.expect("We checked that both directions are available at the start");
+               Some((DirectedChannelInfo::new(self, dir, outbound), target))
        }
 
        /// Returns a [`ChannelUpdateInfo`] based on the direction implied by the channel_flag.
@@ -1677,7 +1710,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
                self.add_channel_between_nodes(short_channel_id, channel_info, None)
        }
 
-       fn add_channel_between_nodes(&self, short_channel_id: u64, channel_info: ChannelInfo, utxo_value: Option<u64>) -> Result<(), LightningError> {
+       fn add_channel_between_nodes(&self, short_channel_id: u64, channel_info: ChannelInfo, utxo_value: Option<Amount>) -> Result<(), LightningError> {
                let mut channels = self.channels.write().unwrap();
                let mut nodes = self.nodes.write().unwrap();
 
@@ -1818,7 +1851,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
                        one_to_two: None,
                        node_two: msg.node_id_2,
                        two_to_one: None,
-                       capacity_sats: utxo_value,
+                       capacity_sats: utxo_value.map(|a| a.to_sat()),
                        announcement_message: if msg.excess_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY
                                { full_msg.cloned() } else { None },
                        announcement_received_time,
@@ -2242,7 +2275,8 @@ pub(crate) mod tests {
        use bitcoin::hashes::sha256d::Hash as Sha256dHash;
        use bitcoin::hashes::Hash;
        use bitcoin::hashes::hex::FromHex;
-       use bitcoin::network::constants::Network;
+       use bitcoin::network::Network;
+       use bitcoin::amount::Amount;
        use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::blockdata::script::ScriptBuf;
        use bitcoin::blockdata::transaction::TxOut;
@@ -2335,7 +2369,7 @@ pub(crate) mod tests {
                let node_1_btckey = SecretKey::from_slice(&[40; 32]).unwrap();
                let node_2_btckey = SecretKey::from_slice(&[39; 32]).unwrap();
                make_funding_redeemscript(&PublicKey::from_secret_key(secp_ctx, &node_1_btckey),
-                       &PublicKey::from_secret_key(secp_ctx, &node_2_btckey)).to_v0_p2wsh()
+                       &PublicKey::from_secret_key(secp_ctx, &node_2_btckey)).to_p2wsh()
        }
 
        pub(crate) fn get_signed_channel_update<F: Fn(&mut UnsignedChannelUpdate)>(f: F, node_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> ChannelUpdate {
@@ -2468,7 +2502,7 @@ pub(crate) mod tests {
 
                // Now test if the transaction is found in the UTXO set and the script is correct.
                *chain_source.utxo_ret.lock().unwrap() =
-                       UtxoResult::Sync(Ok(TxOut { value: 0, script_pubkey: good_script.clone() }));
+                       UtxoResult::Sync(Ok(TxOut { value: Amount::ZERO, script_pubkey: good_script.clone() }));
                let valid_announcement = get_signed_channel_announcement(|unsigned_announcement| {
                        unsigned_announcement.short_channel_id += 2;
                }, node_1_privkey, node_2_privkey, &secp_ctx);
@@ -2487,7 +2521,7 @@ pub(crate) mod tests {
                // If we receive announcement for the same channel, once we've validated it against the
                // chain, we simply ignore all new (duplicate) announcements.
                *chain_source.utxo_ret.lock().unwrap() =
-                       UtxoResult::Sync(Ok(TxOut { value: 0, script_pubkey: good_script }));
+                       UtxoResult::Sync(Ok(TxOut { value: Amount::ZERO, script_pubkey: good_script }));
                match gossip_sync.handle_channel_announcement(&valid_announcement) {
                        Ok(_) => panic!(),
                        Err(e) => assert_eq!(e.err, "Already have chain-validated channel")
@@ -2564,7 +2598,7 @@ pub(crate) mod tests {
                let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
                let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
 
-               let amount_sats = 1000_000;
+               let amount_sats = Amount::from_sat(1000_000);
                let short_channel_id;
 
                {
@@ -2628,7 +2662,7 @@ pub(crate) mod tests {
                };
 
                let valid_channel_update = get_signed_channel_update(|unsigned_channel_update| {
-                       unsigned_channel_update.htlc_maximum_msat = amount_sats * 1000 + 1;
+                       unsigned_channel_update.htlc_maximum_msat = amount_sats.to_sat() * 1000 + 1;
                        unsigned_channel_update.timestamp += 110;
                }, node_1_privkey, &secp_ctx);
                match gossip_sync.handle_channel_update(&valid_channel_update) {
index 7fff856345c84c8f8fadadfe262992a4a4e8f6bc..9e6d8e2afd2f763f90ccff2e2eab517d1f30a573 100644 (file)
@@ -14,4 +14,4 @@ pub mod gossip;
 pub mod router;
 pub mod scoring;
 #[cfg(test)]
-mod test_utils;
+pub(crate) mod test_utils;
index de970d1da165e3149a2de364c55d3bed9025dda8..5319a3fed4820a219f60aac38406e167c0acde5a 100644 (file)
 
 use bitcoin::secp256k1::{PublicKey, Secp256k1, self};
 
-use crate::blinded_path::{BlindedHop, BlindedPath};
-use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs};
-use crate::ln::PaymentHash;
-use crate::ln::channelmanager::{ChannelDetails, PaymentId, MIN_FINAL_CLTV_EXPIRY_DELTA};
+use crate::blinded_path::{BlindedHop, BlindedPath, Direction, IntroductionNode};
+use crate::blinded_path::message;
+use crate::blinded_path::payment::{ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs, self};
+use crate::ln::{PaymentHash, PaymentPreimage};
+use crate::ln::channelmanager::{ChannelDetails, PaymentId, MIN_FINAL_CLTV_EXPIRY_DELTA, RecipientOnionFields};
 use crate::ln::features::{BlindedHopFeatures, Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures};
 use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
+use crate::ln::onion_utils;
 use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice};
 use crate::onion_message::messenger::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath};
 use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees};
@@ -121,7 +123,7 @@ impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, ES: Deref, S: Deref,
                                        max_cltv_expiry: tlvs.payment_constraints.max_cltv_expiry + cltv_expiry_delta,
                                        htlc_minimum_msat: details.inbound_htlc_minimum_msat.unwrap_or(0),
                                };
-                               Some(ForwardNode {
+                               Some(payment::ForwardNode {
                                        tlvs: ForwardTlvs {
                                                short_channel_id,
                                                payment_relay,
@@ -170,7 +172,7 @@ impl< G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, ES: Deref, S: Deref,
        fn create_blinded_paths<
                T: secp256k1::Signing + secp256k1::Verification
        > (
-               &self, recipient: PublicKey, peers: Vec<PublicKey>, secp_ctx: &Secp256k1<T>,
+               &self, recipient: PublicKey, peers: Vec<message::ForwardNode>, secp_ctx: &Secp256k1<T>,
        ) -> Result<Vec<BlindedPath>, ()> {
                self.message_router.create_blinded_paths(recipient, peers, secp_ctx)
        }
@@ -603,6 +605,17 @@ impl RouteParameters {
        pub fn from_payment_params_and_value(payment_params: PaymentParameters, final_value_msat: u64) -> Self {
                Self { payment_params, final_value_msat, max_total_routing_fee_msat: Some(final_value_msat / 100 + 50_000) }
        }
+
+       /// Sets the maximum number of hops that can be included in a payment path, based on the provided
+       /// [`RecipientOnionFields`] and blinded paths.
+       pub fn set_max_path_length(
+               &mut self, recipient_onion: &RecipientOnionFields, is_keysend: bool, best_block_height: u32
+       ) -> Result<(), ()> {
+               let keysend_preimage_opt = is_keysend.then(|| PaymentPreimage([42; 32]));
+               onion_utils::set_max_path_length(
+                       self, recipient_onion, keysend_preimage_opt, best_block_height
+               )
+       }
 }
 
 impl Writeable for RouteParameters {
@@ -654,6 +667,8 @@ const DEFAULT_MAX_CHANNEL_SATURATION_POW_HALF: u8 = 2;
 // The median hop CLTV expiry delta currently seen in the network.
 const MEDIAN_HOP_CLTV_EXPIRY_DELTA: u32 = 40;
 
+/// Estimated maximum number of hops that can be included in a payment path. May be inaccurate if
+/// payment metadata, custom TLVs, or blinded paths are included in the payment.
 // During routing, we only consider paths shorter than our maximum length estimate.
 // In the TLV onion format, there is no fixed maximum length, but the `hop_payloads`
 // field is always 1300 bytes. As the `tlv_payload` for each hop may vary in length, we have to
@@ -665,7 +680,7 @@ const MEDIAN_HOP_CLTV_EXPIRY_DELTA: u32 = 40;
 // (payment_secret and total_msat) = 93 bytes for the final hop.
 // Since the length of the potentially included `payment_metadata` is unknown to us, we round
 // down from (1300-93) / 61 = 19.78... to arrive at a conservative estimate of 19.
-const MAX_PATH_LENGTH_ESTIMATE: u8 = 19;
+pub const MAX_PATH_LENGTH_ESTIMATE: u8 = 19;
 
 /// Information used to route a payment.
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
@@ -684,6 +699,10 @@ pub struct PaymentParameters {
        /// Defaults to [`DEFAULT_MAX_PATH_COUNT`].
        pub max_path_count: u8,
 
+       /// The maximum number of [`Path::hops`] in any returned path.
+       /// Defaults to [`MAX_PATH_LENGTH_ESTIMATE`].
+       pub max_path_length: u8,
+
        /// Selects the maximum share of a channel's total capacity which will be sent over a channel,
        /// as a power of 1/2. A higher value prefers to send the payment using more MPP parts whereas
        /// a lower value prefers to send larger MPP parts, potentially saturating channels and
@@ -730,6 +749,7 @@ impl Writeable for PaymentParameters {
                        (8, *blinded_hints, optional_vec),
                        (9, self.payee.final_cltv_expiry_delta(), option),
                        (11, self.previously_failed_blinded_path_idxs, required_vec),
+                       (13, self.max_path_length, required),
                });
                Ok(())
        }
@@ -749,6 +769,7 @@ impl ReadableArgs<u32> for PaymentParameters {
                        (8, blinded_route_hints, optional_vec),
                        (9, final_cltv_expiry_delta, (default_value, default_final_cltv_expiry_delta)),
                        (11, previously_failed_blinded_path_idxs, optional_vec),
+                       (13, max_path_length, (default_value, MAX_PATH_LENGTH_ESTIMATE)),
                });
                let blinded_route_hints = blinded_route_hints.unwrap_or(vec![]);
                let payee = if blinded_route_hints.len() != 0 {
@@ -773,6 +794,7 @@ impl ReadableArgs<u32> for PaymentParameters {
                        expiry_time,
                        previously_failed_channels: previously_failed_channels.unwrap_or(Vec::new()),
                        previously_failed_blinded_path_idxs: previously_failed_blinded_path_idxs.unwrap_or(Vec::new()),
+                       max_path_length: _init_tlv_based_struct_field!(max_path_length, (default_value, unused)),
                })
        }
 }
@@ -789,6 +811,7 @@ impl PaymentParameters {
                        expiry_time: None,
                        max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
                        max_path_count: DEFAULT_MAX_PATH_COUNT,
+                       max_path_length: MAX_PATH_LENGTH_ESTIMATE,
                        max_channel_saturation_power_of_half: DEFAULT_MAX_CHANNEL_SATURATION_POW_HALF,
                        previously_failed_channels: Vec::new(),
                        previously_failed_blinded_path_idxs: Vec::new(),
@@ -828,6 +851,7 @@ impl PaymentParameters {
                        expiry_time: None,
                        max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
                        max_path_count: DEFAULT_MAX_PATH_COUNT,
+                       max_path_length: MAX_PATH_LENGTH_ESTIMATE,
                        max_channel_saturation_power_of_half: DEFAULT_MAX_CHANNEL_SATURATION_POW_HALF,
                        previously_failed_channels: Vec::new(),
                        previously_failed_blinded_path_idxs: Vec::new(),
@@ -1139,11 +1163,11 @@ pub struct FirstHopCandidate<'a> {
        ///
        /// [`find_route`] validates this prior to constructing a [`CandidateRouteHop`].
        ///
-       /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+       /// This is not exported to bindings users as lifetimes are not expressible in most languages.
        pub details: &'a ChannelDetails,
        /// The node id of the payer, which is also the source side of this candidate route hop.
        ///
-       /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+       /// This is not exported to bindings users as lifetimes are not expressible in most languages.
        pub payer_node_id: &'a NodeId,
        /// A unique ID which describes the payer.
        ///
@@ -1163,7 +1187,7 @@ pub struct PublicHopCandidate<'a> {
        /// Information about the channel, including potentially its capacity and
        /// direction-specific information.
        ///
-       /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+       /// This is not exported to bindings users as lifetimes are not expressible in most languages.
        pub info: DirectedChannelInfo<'a>,
        /// The short channel ID of the channel, i.e. the identifier by which we refer to this
        /// channel.
@@ -1175,11 +1199,11 @@ pub struct PublicHopCandidate<'a> {
 pub struct PrivateHopCandidate<'a> {
        /// Information about the private hop communicated via BOLT 11.
        ///
-       /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+       /// This is not exported to bindings users as lifetimes are not expressible in most languages.
        pub hint: &'a RouteHintHop,
        /// Node id of the next hop in BOLT 11 route hint.
        ///
-       /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+       /// This is not exported to bindings users as lifetimes are not expressible in most languages.
        pub target_node_id: &'a NodeId,
        /// A unique ID which describes the source node of the hop (further from the payment target).
        ///
@@ -1196,10 +1220,15 @@ pub struct PrivateHopCandidate<'a> {
 /// A [`CandidateRouteHop::Blinded`] entry.
 #[derive(Clone, Debug)]
 pub struct BlindedPathCandidate<'a> {
+       /// The node id of the introduction node, resolved from either the [`NetworkGraph`] or first
+       /// hops.
+       ///
+       /// This is not exported to bindings users as lifetimes are not expressible in most languages.
+       pub source_node_id: &'a NodeId,
        /// Information about the blinded path including the fee, HTLC amount limits, and
        /// cryptographic material required to build an HTLC through the given path.
        ///
-       /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+       /// This is not exported to bindings users as lifetimes are not expressible in most languages.
        pub hint: &'a (BlindedPayInfo, BlindedPath),
        /// Index of the hint in the original list of blinded hints.
        ///
@@ -1217,12 +1246,17 @@ pub struct BlindedPathCandidate<'a> {
 /// A [`CandidateRouteHop::OneHopBlinded`] entry.
 #[derive(Clone, Debug)]
 pub struct OneHopBlindedPathCandidate<'a> {
+       /// The node id of the introduction node, resolved from either the [`NetworkGraph`] or first
+       /// hops.
+       ///
+       /// This is not exported to bindings users as lifetimes are not expressible in most languages.
+       pub source_node_id: &'a NodeId,
        /// Information about the blinded path including the fee, HTLC amount limits, and
        /// cryptographic material required to build an HTLC terminating with the given path.
        ///
        /// Note that the [`BlindedPayInfo`] is ignored here.
        ///
-       /// This is not exported to bindings users as lifetimes are not expressable in most languages.
+       /// This is not exported to bindings users as lifetimes are not expressible in most languages.
        pub hint: &'a (BlindedPayInfo, BlindedPath),
        /// Index of the hint in the original list of blinded hints.
        ///
@@ -1458,8 +1492,8 @@ impl<'a> CandidateRouteHop<'a> {
                        CandidateRouteHop::FirstHop(hop) => *hop.payer_node_id,
                        CandidateRouteHop::PublicHop(hop) => *hop.info.source(),
                        CandidateRouteHop::PrivateHop(hop) => hop.hint.src_node_id.into(),
-                       CandidateRouteHop::Blinded(hop) => hop.hint.1.introduction_node_id.into(),
-                       CandidateRouteHop::OneHopBlinded(hop) => hop.hint.1.introduction_node_id.into(),
+                       CandidateRouteHop::Blinded(hop) => *hop.source_node_id,
+                       CandidateRouteHop::OneHopBlinded(hop) => *hop.source_node_id,
                }
        }
        /// Returns the target node id of this hop, if known.
@@ -1563,9 +1597,17 @@ impl<'a> NodeCounters<'a> {
                        self.private_node_id_to_node_counter.len() as u32
        }
 
-       fn node_counter_from_pubkey(&self, pubkey: &PublicKey) -> Option<&(NodeId, u32)> {
+       fn private_node_counter_from_pubkey(&self, pubkey: &PublicKey) -> Option<&(NodeId, u32)> {
                self.private_hop_key_cache.get(pubkey)
        }
+
+       fn node_counter_from_id(&self, node_id: &NodeId) -> Option<(&NodeId, u32)> {
+               self.private_node_id_to_node_counter.get_key_value(node_id).map(|(a, b)| (a, *b))
+                       .or_else(|| {
+                               self.network_graph.nodes().get_key_value(node_id)
+                                       .map(|(node_id, node)| (node_id, node.node_counter))
+                       })
+       }
 }
 
 #[inline]
@@ -1838,8 +1880,20 @@ impl<'a> fmt::Display for LoggedCandidateHop<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                match self.0 {
                        CandidateRouteHop::Blinded(BlindedPathCandidate { hint, .. }) | CandidateRouteHop::OneHopBlinded(OneHopBlindedPathCandidate { hint, .. }) => {
-                               "blinded route hint with introduction node id ".fmt(f)?;
-                               hint.1.introduction_node_id.fmt(f)?;
+                               "blinded route hint with introduction node ".fmt(f)?;
+                               match &hint.1.introduction_node {
+                                       IntroductionNode::NodeId(pubkey) => write!(f, "id {}", pubkey)?,
+                                       IntroductionNode::DirectedShortChannelId(direction, scid) => {
+                                               match direction {
+                                                       Direction::NodeOne => {
+                                                               write!(f, "one on channel with SCID {}", scid)?;
+                                                       },
+                                                       Direction::NodeTwo => {
+                                                               write!(f, "two on channel with SCID {}", scid)?;
+                                                       },
+                                               }
+                                       }
+                               }
                                " and blinding point ".fmt(f)?;
                                hint.1.blinding_point.fmt(f)
                        },
@@ -1940,6 +1994,7 @@ pub(crate) fn get_route<L: Deref, S: ScoreLookUp>(
 where L::Target: Logger {
 
        let payment_params = &route_params.payment_params;
+       let max_path_length = core::cmp::min(payment_params.max_path_length, MAX_PATH_LENGTH_ESTIMATE);
        let final_value_msat = route_params.final_value_msat;
        // 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,
@@ -1965,34 +2020,6 @@ where L::Target: Logger {
                return Err(LightningError{err: "Cannot send a payment of 0 msat".to_owned(), action: ErrorAction::IgnoreError});
        }
 
-       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 == *node_id {
-                                               return Err(LightningError{err: "Route hint cannot have the payee as the source.".to_owned(), action: ErrorAction::IgnoreError});
-                                       }
-                               }
-                       }
-               },
-               Payee::Blinded { route_hints, .. } => {
-                       if route_hints.iter().all(|(_, path)| &path.introduction_node_id == our_node_pubkey) {
-                               return Err(LightningError{err: "Cannot generate a route to blinded paths if we are the introduction node to all of them".to_owned(), action: ErrorAction::IgnoreError});
-                       }
-                       for (_, blinded_path) in route_hints.iter() {
-                               if blinded_path.blinded_hops.len() == 0 {
-                                       return Err(LightningError{err: "0-hop blinded path provided".to_owned(), action: ErrorAction::IgnoreError});
-                               } else if &blinded_path.introduction_node_id == our_node_pubkey {
-                                       log_info!(logger, "Got blinded path with ourselves as the introduction node, ignoring");
-                               } else if blinded_path.blinded_hops.len() == 1 &&
-                                       route_hints.iter().any( |(_, p)| p.blinded_hops.len() == 1
-                                               && p.introduction_node_id != blinded_path.introduction_node_id)
-                               {
-                                       return Err(LightningError{err: format!("1-hop blinded paths must all have matching introduction node ids"), 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});
@@ -2075,12 +2102,29 @@ where L::Target: Logger {
 
        let max_total_routing_fee_msat = route_params.max_total_routing_fee_msat.unwrap_or(u64::max_value());
 
-       log_trace!(logger, "Searching for a route from payer {} to {} {} MPP and {} first hops {}overriding the network graph with a fee limit of {} msat",
+       let first_hop_count = first_hops.map(|hops| hops.len()).unwrap_or(0);
+       log_trace!(logger, "Searching for a route from payer {} to {} {} MPP and {} first hops {}overriding the network graph of {} nodes and {} channels with a fee limit of {} msat",
                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 " },
+               first_hop_count, if first_hops.is_some() { "" } else { "not " },
+               network_graph.nodes().len(), network_graph.channels().len(),
                max_total_routing_fee_msat);
 
+       if first_hop_count < 10 {
+               if let Some(hops) = first_hops {
+                       for hop in hops {
+                               log_trace!(
+                                       logger,
+                                       " First hop through {}/{} can send between {}msat and {}msat (inclusive).",
+                                       hop.counterparty.node_id,
+                                       hop.get_outbound_payment_scid().unwrap_or(0),
+                                       hop.next_outbound_htlc_minimum_msat,
+                                       hop.next_outbound_htlc_limit_msat
+                               );
+                       }
+               }
+       }
+
        let mut node_counters = NodeCountersBuilder::new(&network_graph);
 
        let payer_node_counter = node_counters.node_counter_from_pubkey(*our_node_pubkey);
@@ -2106,11 +2150,12 @@ where L::Target: Logger {
                        if chan.counterparty.node_id == *our_node_pubkey {
                                return Err(LightningError{err: "First hop cannot have our_node_pubkey as a destination.".to_owned(), action: ErrorAction::IgnoreError});
                        }
-                       let counterparty_node_id = NodeId::from_pubkey(&chan.counterparty.node_id);
+                       let counterparty_id = NodeId::from_pubkey(&chan.counterparty.node_id);
                        first_hop_targets
-                               .entry(counterparty_node_id)
+                               .entry(counterparty_id)
                                .or_insert_with(|| {
-                                       let node_counter = node_counters.node_counter_from_id(counterparty_node_id);
+                                       // Make sure there's a counter assigned for the counterparty
+                                       let node_counter = node_counters.node_counter_from_id(counterparty_id);
                                        (Vec::new(), node_counter)
                                })
                                .0.push(chan);
@@ -2122,6 +2167,64 @@ where L::Target: Logger {
 
        let node_counters = node_counters.build();
 
+       let introduction_node_id_cache = payment_params.payee.blinded_route_hints().iter()
+               .map(|(_, path)| {
+                       match &path.introduction_node {
+                               IntroductionNode::NodeId(pubkey) => {
+                                       node_counters.node_counter_from_id(&NodeId::from_pubkey(&pubkey))
+                               },
+                               IntroductionNode::DirectedShortChannelId(_, scid) => {
+                                       let node_id = if let Some(node_id) = path.public_introduction_node_id(network_graph) {
+                                               Some(node_id)
+                                       } else {
+                                               first_hop_targets.iter().find(|(_, (channels, _))|
+                                                       channels
+                                                               .iter()
+                                                               .any(|details| Some(*scid) == details.get_outbound_payment_scid())
+                                               ).map(|(counterparty_node_id, _)| counterparty_node_id)
+                                       };
+                                       match node_id {
+                                               Some(node_id) => node_counters.node_counter_from_id(&node_id),
+                                               None => None,
+                                       }
+                               },
+                       }
+               })
+               .collect::<Vec<_>>();
+       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 == *node_id {
+                                               return Err(LightningError{err: "Route hint cannot have the payee as the source.".to_owned(), action: ErrorAction::IgnoreError});
+                                       }
+                               }
+                       }
+               },
+               Payee::Blinded { route_hints, .. } => {
+                       if introduction_node_id_cache.iter().all(|info_opt| info_opt.map(|(a, _)| a) == Some(&our_node_id)) {
+                               return Err(LightningError{err: "Cannot generate a route to blinded paths if we are the introduction node to all of them".to_owned(), action: ErrorAction::IgnoreError});
+                       }
+                       for ((_, blinded_path), info_opt) in route_hints.iter().zip(introduction_node_id_cache.iter()) {
+                               if blinded_path.blinded_hops.len() == 0 {
+                                       return Err(LightningError{err: "0-hop blinded path provided".to_owned(), action: ErrorAction::IgnoreError});
+                               }
+                               if info_opt.is_none() { continue }
+                               let introduction_node_id = info_opt.unwrap().0;
+                               if *introduction_node_id == our_node_id {
+                                       log_info!(logger, "Got blinded path with ourselves as the introduction node, ignoring");
+                               } else if blinded_path.blinded_hops.len() == 1 &&
+                                       route_hints
+                                               .iter().zip(introduction_node_id_cache.iter())
+                                               .filter(|((_, p), _)| p.blinded_hops.len() == 1)
+                                               .any(|(_, p_introduction_node_id)| p_introduction_node_id != info_opt)
+                               {
+                                       return Err(LightningError{err: format!("1-hop blinded paths must all have matching introduction node ids"), action: ErrorAction::IgnoreError});
+                               }
+                       }
+               }
+       }
+
        // The main heap containing all candidate next-hops sorted by their score (max(fee,
        // htlc_minimum)). Ideally this would be a heap which allowed cheap score reduction instead of
        // adding duplicate entries when we find a better path to a given node.
@@ -2236,8 +2339,9 @@ where L::Target: Logger {
                                        // Verify the liquidity offered by this channel complies to the minimal contribution.
                                        let contributes_sufficient_value = available_value_contribution_msat >= minimal_value_contribution_msat;
                                        // Do not consider candidate hops that would exceed the maximum path length.
-                                       let path_length_to_node = $next_hops_path_length + 1;
-                                       let exceeds_max_path_length = path_length_to_node > MAX_PATH_LENGTH_ESTIMATE;
+                                       let path_length_to_node = $next_hops_path_length
+                                               + if $candidate.blinded_hint_idx().is_some() { 0 } else { 1 };
+                                       let exceeds_max_path_length = path_length_to_node > max_path_length;
 
                                        // Do not consider candidates that exceed the maximum total cltv expiry limit.
                                        // In order to already account for some of the privacy enhancing random CLTV
@@ -2292,14 +2396,9 @@ where L::Target: Logger {
                                        // around again with a higher amount.
                                        if !contributes_sufficient_value {
                                                if should_log_candidate {
-                                                       log_trace!(logger, "Ignoring {} due to insufficient value contribution.", LoggedCandidateHop(&$candidate));
-
-                                                       if let Some(details) = first_hop_details {
-                                                               log_trace!(logger,
-                                                                       "First hop candidate next_outbound_htlc_limit_msat: {}",
-                                                                       details.next_outbound_htlc_limit_msat,
-                                                               );
-                                                       }
+                                                       log_trace!(logger, "Ignoring {} due to insufficient value contribution (channel max {:?}).",
+                                                               LoggedCandidateHop(&$candidate),
+                                                               effective_capacity);
                                                }
                                                num_ignored_value_contribution += 1;
                                        } else if exceeds_max_path_length {
@@ -2328,15 +2427,8 @@ where L::Target: Logger {
                                        } else if may_overpay_to_meet_path_minimum_msat {
                                                if should_log_candidate {
                                                        log_trace!(logger,
-                                                               "Ignoring {} to avoid overpaying to meet htlc_minimum_msat limit.",
-                                                               LoggedCandidateHop(&$candidate));
-
-                                                       if let Some(details) = first_hop_details {
-                                                               log_trace!(logger,
-                                                                       "First hop candidate next_outbound_htlc_minimum_msat: {}",
-                                                                       details.next_outbound_htlc_minimum_msat,
-                                                               );
-                                                       }
+                                                               "Ignoring {} to avoid overpaying to meet htlc_minimum_msat limit ({}).",
+                                                               LoggedCandidateHop(&$candidate), $candidate.htlc_minimum_msat());
                                                }
                                                num_ignored_avoid_overpayment += 1;
                                                hit_minimum_limit = true;
@@ -2682,28 +2774,18 @@ where L::Target: Logger {
                // earlier than general path finding, they will be somewhat prioritized, although currently
                // it matters only if the fees are exactly the same.
                for (hint_idx, hint) in payment_params.payee.blinded_route_hints().iter().enumerate() {
-                       let intro_node_id = NodeId::from_pubkey(&hint.1.introduction_node_id);
-                       let intro_node_counter_opt =
-                               // Only add the hops in this route to our candidate set if either
-                               // we have a direct channel to the first hop or the first hop is
-                               // in the regular network graph.
-                               network_nodes.get(&intro_node_id).map(|node| node.node_counter)
-                               .or(
-                                       first_hop_targets.get(&intro_node_id).map(|(_, node_counter)| *node_counter)
-                               );
-                       if intro_node_counter_opt.is_none() || our_node_id == intro_node_id { continue }
+                       // Only add the hops in this route to our candidate set if either
+                       // we have a direct channel to the first hop or the first hop is
+                       // in the regular network graph.
+                       let source_node_opt = introduction_node_id_cache[hint_idx];
+                       let (source_node_id, source_node_counter) = if let Some(v) = source_node_opt { v } else { continue };
+                       if our_node_id == *source_node_id { continue }
                        let candidate = if hint.1.blinded_hops.len() == 1 {
-                               CandidateRouteHop::OneHopBlinded(OneHopBlindedPathCandidate {
-                                       hint,
-                                       hint_idx,
-                                       source_node_counter: intro_node_counter_opt.unwrap(),
-                               })
+                               CandidateRouteHop::OneHopBlinded(
+                                       OneHopBlindedPathCandidate { source_node_counter, source_node_id, hint, hint_idx }
+                               )
                        } else {
-                               CandidateRouteHop::Blinded(BlindedPathCandidate {
-                                       hint,
-                                       hint_idx,
-                                       source_node_counter: intro_node_counter_opt.unwrap(),
-                               })
+                               CandidateRouteHop::Blinded(BlindedPathCandidate { source_node_counter, source_node_id, hint, hint_idx })
                        };
                        let mut path_contribution_msat = path_value_msat;
                        if let Some(hop_used_msat) = add_entry!(&candidate,
@@ -2711,9 +2793,10 @@ where L::Target: Logger {
                        {
                                path_contribution_msat = hop_used_msat;
                        } else { continue }
-                       if let Some((first_channels, peer_node_counter)) = first_hop_targets.get_mut(&NodeId::from_pubkey(&hint.1.introduction_node_id)) {
-                               sort_first_hop_channels(first_channels, &used_liquidities, recommended_value_msat,
-                                       our_node_pubkey);
+                       if let Some((first_channels, peer_node_counter)) = first_hop_targets.get_mut(source_node_id) {
+                               sort_first_hop_channels(
+                                       first_channels, &used_liquidities, recommended_value_msat, our_node_pubkey
+                               );
                                for details in first_channels {
                                        let first_hop_candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {
                                                details, payer_node_id: &our_node_id, payer_node_counter,
@@ -2725,9 +2808,8 @@ where L::Target: Logger {
                                        };
                                        let path_min = candidate.htlc_minimum_msat().saturating_add(
                                                compute_fees_saturating(candidate.htlc_minimum_msat(), candidate.fees()));
-                                       add_entry!(&first_hop_candidate, blinded_path_fee,
-                                               path_contribution_msat, path_min, 0_u64, candidate.cltv_expiry_delta(),
-                                               candidate.blinded_path().map_or(1, |bp| bp.blinded_hops.len() as u8));
+                                       add_entry!(&first_hop_candidate, blinded_path_fee, path_contribution_msat, path_min,
+                                               0_u64, candidate.cltv_expiry_delta(), 0);
                                }
                        }
                }
@@ -2758,10 +2840,10 @@ where L::Target: Logger {
 
                                for (idx, (hop, prev_hop_id)) in hop_iter.zip(prev_hop_iter).enumerate() {
                                        let (target, private_target_node_counter) =
-                                               node_counters.node_counter_from_pubkey(&prev_hop_id)
+                                               node_counters.private_node_counter_from_pubkey(&prev_hop_id)
                                                .expect("node_counter_from_pubkey is called on all unblinded_route_hints keys during setup, so is always Some here");
                                        let (_src_id, private_source_node_counter) =
-                                               node_counters.node_counter_from_pubkey(&hop.src_node_id)
+                                               node_counters.private_node_counter_from_pubkey(&hop.src_node_id)
                                                .expect("node_counter_from_pubkey is called on all unblinded_route_hints keys during setup, so is always Some here");
 
                                        if let Some((first_channels, _)) = first_hop_targets.get(target) {
@@ -2820,8 +2902,9 @@ where L::Target: Logger {
 
                                        // Searching for a direct channel between last checked hop and first_hop_targets
                                        if let Some((first_channels, peer_node_counter)) = first_hop_targets.get_mut(target) {
-                                               sort_first_hop_channels(first_channels, &used_liquidities,
-                                                       recommended_value_msat, our_node_pubkey);
+                                               sort_first_hop_channels(
+                                                       first_channels, &used_liquidities, recommended_value_msat, our_node_pubkey
+                                               );
                                                for details in first_channels {
                                                        let first_hop_candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {
                                                                details, payer_node_id: &our_node_id, payer_node_counter,
@@ -2868,8 +2951,9 @@ where L::Target: Logger {
                                                // always assumes that the third argument is a node to which we have a
                                                // path.
                                                if let Some((first_channels, peer_node_counter)) = first_hop_targets.get_mut(&NodeId::from_pubkey(&hop.src_node_id)) {
-                                                       sort_first_hop_channels(first_channels, &used_liquidities,
-                                                               recommended_value_msat, our_node_pubkey);
+                                                       sort_first_hop_channels(
+                                                               first_channels, &used_liquidities, recommended_value_msat, our_node_pubkey
+                                                       );
                                                        for details in first_channels {
                                                                let first_hop_candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {
                                                                        details, payer_node_id: &our_node_id, payer_node_counter,
@@ -3417,7 +3501,7 @@ fn build_route_from_hops_internal<L: Deref>(
 
 #[cfg(test)]
 mod tests {
-       use crate::blinded_path::{BlindedHop, BlindedPath};
+       use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
        use crate::routing::gossip::{NetworkGraph, P2PGossipSync, NodeId, EffectiveCapacity};
        use crate::routing::utxo::UtxoResult;
        use crate::routing::router::{get_route, build_route_from_hops_internal, add_random_cltv_offset, default_node_features,
@@ -3427,7 +3511,7 @@ mod tests {
        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::sign::EntropySource;
-       use crate::ln::ChannelId;
+       use crate::ln::types::ChannelId;
        use crate::ln::features::{BlindedHopFeatures, ChannelFeatures, InitFeatures, NodeFeatures};
        use crate::ln::msgs::{ErrorAction, LightningError, UnsignedChannelUpdate, MAX_VALUE_MSAT};
        use crate::ln::channelmanager;
@@ -3439,8 +3523,9 @@ mod tests {
        #[cfg(c_bindings)]
        use crate::util::ser::Writer;
 
+       use bitcoin::amount::Amount;
        use bitcoin::hashes::Hash;
-       use bitcoin::network::constants::Network;
+       use bitcoin::network::Network;
        use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::blockdata::script::Builder;
        use bitcoin::blockdata::opcodes;
@@ -3453,8 +3538,6 @@ mod tests {
        use crate::prelude::*;
        use crate::sync::Arc;
 
-       use core::convert::TryInto;
-
        fn get_channel_details(short_channel_id: Option<u64>, node_id: PublicKey,
                        features: InitFeatures, outbound_capacity_msat: u64) -> channelmanager::ChannelDetails {
                channelmanager::ChannelDetails {
@@ -3499,7 +3582,7 @@ mod tests {
        fn simple_route_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[2], 42);
+               let mut payment_params = PaymentParameters::from_node_id(nodes[2], 42);
                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();
@@ -3514,7 +3597,8 @@ mod tests {
                                assert_eq!(err, "Cannot send a payment of 0 msat");
                } else { panic!(); }
 
-               let route_params = RouteParameters::from_payment_params_and_value(payment_params, 100);
+               payment_params.max_path_length = 2;
+               let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, 100);
                let route = get_route(&our_id, &route_params, &network_graph.read_only(), None,
                        Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].hops.len(), 2);
@@ -3532,6 +3616,10 @@ mod tests {
                assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 42);
                assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3));
                assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(4));
+
+               route_params.payment_params.max_path_length = 1;
+               get_route(&our_id, &route_params, &network_graph.read_only(), None,
+                       Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes).unwrap_err();
        }
 
        #[test]
@@ -3936,7 +4024,7 @@ mod tests {
                });
 
                // If all the channels require some features we don't understand, route should fail
-               let route_params = RouteParameters::from_payment_params_and_value(payment_params, 100);
+               let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, 100);
                if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id,
                        &route_params, &network_graph.read_only(), None, Arc::clone(&logger), &scorer,
                        &Default::default(), &random_seed_bytes) {
@@ -3946,6 +4034,7 @@ mod tests {
                // 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)];
+               route_params.payment_params.max_path_length = 2;
                let route = get_route(&our_id, &route_params, &network_graph.read_only(),
                        Some(&our_chans.iter().collect::<Vec<_>>()), Arc::clone(&logger), &scorer,
                        &Default::default(), &random_seed_bytes).unwrap();
@@ -4192,8 +4281,9 @@ mod tests {
                        } else { panic!(); }
                }
 
-               let payment_params = PaymentParameters::from_node_id(nodes[6], 42)
+               let mut payment_params = PaymentParameters::from_node_id(nodes[6], 42)
                        .with_route_hints(last_hops_multi_private_channels(&nodes)).unwrap();
+               payment_params.max_path_length = 5;
                let route_params = RouteParameters::from_payment_params_and_value(payment_params, 100);
                let route = get_route(&our_id, &route_params, &network_graph.read_only(), None,
                        Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes).unwrap();
@@ -4351,7 +4441,8 @@ mod tests {
                let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
                let random_seed_bytes = keys_manager.get_secure_random_bytes();
                // Test through channels 2, 3, 0xff00, 0xff01.
-               // Test shows that multiple hop hints are considered.
+               // Test shows that multi-hop route hints are considered and factored correctly into the
+               // max path length.
 
                // Disabling channels 6 & 7 by flags=2
                update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
@@ -4379,7 +4470,8 @@ mod tests {
                        excess_data: Vec::new()
                });
 
-               let route_params = RouteParameters::from_payment_params_and_value(payment_params, 100);
+               let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, 100);
+               route_params.payment_params.max_path_length = 4;
                let route = get_route(&our_id, &route_params, &network_graph.read_only(), None,
                        Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes).unwrap();
                assert_eq!(route.paths[0].hops.len(), 4);
@@ -4411,6 +4503,9 @@ mod tests {
                assert_eq!(route.paths[0].hops[3].cltv_expiry_delta, 42);
                assert_eq!(route.paths[0].hops[3].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet
                assert_eq!(route.paths[0].hops[3].channel_features.le_flags(), &Vec::<u8>::new()); // We can't learn any flags from invoices, sadly
+               route_params.payment_params.max_path_length = 3;
+               get_route(&our_id, &route_params, &network_graph.read_only(), None,
+                       Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes).unwrap_err();
        }
 
        #[test]
@@ -4988,10 +5083,10 @@ mod tests {
                .push_slice(&PublicKey::from_secret_key(&secp_ctx, &privkeys[0]).serialize())
                .push_slice(&PublicKey::from_secret_key(&secp_ctx, &privkeys[2]).serialize())
                .push_opcode(opcodes::all::OP_PUSHNUM_2)
-               .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh();
+               .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_p2wsh();
 
                *chain_monitor.utxo_ret.lock().unwrap() =
-                       UtxoResult::Sync(Ok(TxOut { value: 15, script_pubkey: good_script.clone() }));
+                       UtxoResult::Sync(Ok(TxOut { value: Amount::from_sat(15), script_pubkey: good_script.clone() }));
                gossip_sync.add_utxo_lookup(Some(chain_monitor));
 
                add_channel(&gossip_sync, &secp_ctx, &privkeys[0], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 333);
@@ -5286,7 +5381,7 @@ mod tests {
                // MPP to a 1-hop blinded path for nodes[2]
                let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
                let blinded_path = BlindedPath {
-                       introduction_node_id: nodes[2],
+                       introduction_node: IntroductionNode::NodeId(nodes[2]),
                        blinding_point: ln_test_utils::pubkey(42),
                        blinded_hops: vec![BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() }],
                };
@@ -5304,18 +5399,18 @@ mod tests {
 
                // MPP to 3 2-hop blinded paths
                let mut blinded_path_node_0 = blinded_path.clone();
-               blinded_path_node_0.introduction_node_id = nodes[0];
+               blinded_path_node_0.introduction_node = IntroductionNode::NodeId(nodes[0]);
                blinded_path_node_0.blinded_hops.push(blinded_path.blinded_hops[0].clone());
                let mut node_0_payinfo = blinded_payinfo.clone();
                node_0_payinfo.htlc_maximum_msat = 50_000;
 
                let mut blinded_path_node_7 = blinded_path_node_0.clone();
-               blinded_path_node_7.introduction_node_id = nodes[7];
+               blinded_path_node_7.introduction_node = IntroductionNode::NodeId(nodes[7]);
                let mut node_7_payinfo = blinded_payinfo.clone();
                node_7_payinfo.htlc_maximum_msat = 60_000;
 
                let mut blinded_path_node_1 = blinded_path_node_0.clone();
-               blinded_path_node_1.introduction_node_id = nodes[1];
+               blinded_path_node_1.introduction_node = IntroductionNode::NodeId(nodes[1]);
                let mut node_1_payinfo = blinded_payinfo.clone();
                node_1_payinfo.htlc_maximum_msat = 180_000;
 
@@ -5497,10 +5592,15 @@ mod tests {
                                if let Some(bt) = &path.blinded_tail {
                                        assert_eq!(path.hops.len() + if bt.hops.len() == 1 { 0 } else { 1 }, 2);
                                        if bt.hops.len() > 1 {
-                                               assert_eq!(path.hops.last().unwrap().pubkey,
+                                               let network_graph = network_graph.read_only();
+                                               assert_eq!(
+                                                       NodeId::from_pubkey(&path.hops.last().unwrap().pubkey),
                                                        payment_params.payee.blinded_route_hints().iter()
                                                                .find(|(p, _)| p.htlc_maximum_msat == path.final_value_msat())
-                                                               .map(|(_, p)| p.introduction_node_id).unwrap());
+                                                               .and_then(|(_, p)| p.public_introduction_node_id(&network_graph))
+                                                               .copied()
+                                                               .unwrap()
+                                               );
                                        } else {
                                                assert_eq!(path.hops.last().unwrap().pubkey, nodes[2]);
                                        }
@@ -5599,6 +5699,18 @@ mod tests {
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
                });
+               update_channel(&gossip_sync, &secp_ctx, &privkeys[3], UnsignedChannelUpdate {
+                       chain_hash: ChainHash::using_genesis_block(Network::Testnet),
+                       short_channel_id: 5,
+                       timestamp: 2,
+                       flags: 3, // disable direction 1
+                       cltv_expiry_delta: 0,
+                       htlc_minimum_msat: 0,
+                       htlc_maximum_msat: 200_000,
+                       fee_base_msat: 0,
+                       fee_proportional_millionths: 0,
+                       excess_data: Vec::new()
+               });
 
                // Path via {node7, node2, node4} is channels {12, 13, 6, 11}.
                // Add 100 sats to the capacities of {12, 13}, because these channels
@@ -5776,6 +5888,18 @@ mod tests {
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
                });
+               update_channel(&gossip_sync, &secp_ctx, &privkeys[3], UnsignedChannelUpdate {
+                       chain_hash: ChainHash::using_genesis_block(Network::Testnet),
+                       short_channel_id: 5,
+                       timestamp: 2,
+                       flags: 3, // disable direction 1
+                       cltv_expiry_delta: 0,
+                       htlc_minimum_msat: 0,
+                       htlc_maximum_msat: 200_000,
+                       fee_base_msat: 0,
+                       fee_proportional_millionths: 0,
+                       excess_data: Vec::new()
+               });
 
                // Path via {node7, node2, node4} is channels {12, 13, 6, 11}.
                // Add 100 sats to the capacities of {12, 13}, because these channels
@@ -5949,6 +6073,18 @@ mod tests {
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
                });
+               update_channel(&gossip_sync, &secp_ctx, &privkeys[3], UnsignedChannelUpdate {
+                       chain_hash: ChainHash::using_genesis_block(Network::Testnet),
+                       short_channel_id: 5,
+                       timestamp: 2,
+                       flags: 3, // Disable direction 1
+                       cltv_expiry_delta: 0,
+                       htlc_minimum_msat: 0,
+                       htlc_maximum_msat: 100_000,
+                       fee_base_msat: 0,
+                       fee_proportional_millionths: 0,
+                       excess_data: Vec::new()
+               });
 
                // Path via {node7, node2, node4} is channels {12, 13, 6, 11}.
                // All channels should be 100 sats capacity. But for the fee experiment,
@@ -6341,92 +6477,104 @@ mod tests {
                let payment_params = PaymentParameters::from_node_id(nodes[6], 42);
 
                add_channel(&gossip_sync, &secp_ctx, &our_privkey, &privkeys[1], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6);
-               update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
-                       chain_hash: ChainHash::using_genesis_block(Network::Testnet),
-                       short_channel_id: 6,
-                       timestamp: 1,
-                       flags: 0,
-                       cltv_expiry_delta: (6 << 4) | 0,
-                       htlc_minimum_msat: 0,
-                       htlc_maximum_msat: MAX_VALUE_MSAT,
-                       fee_base_msat: 0,
-                       fee_proportional_millionths: 0,
-                       excess_data: Vec::new()
-               });
+               for (key, flags) in [(&our_privkey, 0), (&privkeys[1], 3)] {
+                       update_channel(&gossip_sync, &secp_ctx, key, UnsignedChannelUpdate {
+                               chain_hash: ChainHash::using_genesis_block(Network::Testnet),
+                               short_channel_id: 6,
+                               timestamp: 1,
+                               flags,
+                               cltv_expiry_delta: (6 << 4) | 0,
+                               htlc_minimum_msat: 0,
+                               htlc_maximum_msat: MAX_VALUE_MSAT,
+                               fee_base_msat: 0,
+                               fee_proportional_millionths: 0,
+                               excess_data: Vec::new()
+                       });
+               }
                add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[1], NodeFeatures::from_le_bytes(id_to_feature_flags(1)), 0);
 
                add_channel(&gossip_sync, &secp_ctx, &privkeys[1], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5);
-               update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
-                       chain_hash: ChainHash::using_genesis_block(Network::Testnet),
-                       short_channel_id: 5,
-                       timestamp: 1,
-                       flags: 0,
-                       cltv_expiry_delta: (5 << 4) | 0,
-                       htlc_minimum_msat: 0,
-                       htlc_maximum_msat: MAX_VALUE_MSAT,
-                       fee_base_msat: 100,
-                       fee_proportional_millionths: 0,
-                       excess_data: Vec::new()
-               });
+               for (key, flags) in [(&privkeys[1], 0), (&privkeys[4], 3)] {
+                       update_channel(&gossip_sync, &secp_ctx, key, UnsignedChannelUpdate {
+                               chain_hash: ChainHash::using_genesis_block(Network::Testnet),
+                               short_channel_id: 5,
+                               timestamp: 1,
+                               flags,
+                               cltv_expiry_delta: (5 << 4) | 0,
+                               htlc_minimum_msat: 0,
+                               htlc_maximum_msat: MAX_VALUE_MSAT,
+                               fee_base_msat: 100,
+                               fee_proportional_millionths: 0,
+                               excess_data: Vec::new()
+                       });
+               }
                add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[4], NodeFeatures::from_le_bytes(id_to_feature_flags(4)), 0);
 
                add_channel(&gossip_sync, &secp_ctx, &privkeys[4], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(4)), 4);
-               update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate {
-                       chain_hash: ChainHash::using_genesis_block(Network::Testnet),
-                       short_channel_id: 4,
-                       timestamp: 1,
-                       flags: 0,
-                       cltv_expiry_delta: (4 << 4) | 0,
-                       htlc_minimum_msat: 0,
-                       htlc_maximum_msat: MAX_VALUE_MSAT,
-                       fee_base_msat: 0,
-                       fee_proportional_millionths: 0,
-                       excess_data: Vec::new()
-               });
+               for (key, flags) in [(&privkeys[4], 0), (&privkeys[3], 3)] {
+                       update_channel(&gossip_sync, &secp_ctx, key, UnsignedChannelUpdate {
+                               chain_hash: ChainHash::using_genesis_block(Network::Testnet),
+                               short_channel_id: 4,
+                               timestamp: 1,
+                               flags,
+                               cltv_expiry_delta: (4 << 4) | 0,
+                               htlc_minimum_msat: 0,
+                               htlc_maximum_msat: MAX_VALUE_MSAT,
+                               fee_base_msat: 0,
+                               fee_proportional_millionths: 0,
+                               excess_data: Vec::new()
+                       });
+               }
                add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[3], NodeFeatures::from_le_bytes(id_to_feature_flags(3)), 0);
 
                add_channel(&gossip_sync, &secp_ctx, &privkeys[3], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 3);
-               update_channel(&gossip_sync, &secp_ctx, &privkeys[3], UnsignedChannelUpdate {
-                       chain_hash: ChainHash::using_genesis_block(Network::Testnet),
-                       short_channel_id: 3,
-                       timestamp: 1,
-                       flags: 0,
-                       cltv_expiry_delta: (3 << 4) | 0,
-                       htlc_minimum_msat: 0,
-                       htlc_maximum_msat: MAX_VALUE_MSAT,
-                       fee_base_msat: 0,
-                       fee_proportional_millionths: 0,
-                       excess_data: Vec::new()
-               });
+               for (key, flags) in [(&privkeys[3], 0), (&privkeys[2], 3)] {
+                       update_channel(&gossip_sync, &secp_ctx, key, UnsignedChannelUpdate {
+                               chain_hash: ChainHash::using_genesis_block(Network::Testnet),
+                               short_channel_id: 3,
+                               timestamp: 1,
+                               flags,
+                               cltv_expiry_delta: (3 << 4) | 0,
+                               htlc_minimum_msat: 0,
+                               htlc_maximum_msat: MAX_VALUE_MSAT,
+                               fee_base_msat: 0,
+                               fee_proportional_millionths: 0,
+                               excess_data: Vec::new()
+                       });
+               }
                add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[2], NodeFeatures::from_le_bytes(id_to_feature_flags(2)), 0);
 
                add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(2)), 2);
-               update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
-                       chain_hash: ChainHash::using_genesis_block(Network::Testnet),
-                       short_channel_id: 2,
-                       timestamp: 1,
-                       flags: 0,
-                       cltv_expiry_delta: (2 << 4) | 0,
-                       htlc_minimum_msat: 0,
-                       htlc_maximum_msat: MAX_VALUE_MSAT,
-                       fee_base_msat: 0,
-                       fee_proportional_millionths: 0,
-                       excess_data: Vec::new()
-               });
+               for (key, flags) in [(&privkeys[2], 0), (&privkeys[4], 3)] {
+                       update_channel(&gossip_sync, &secp_ctx, key, UnsignedChannelUpdate {
+                               chain_hash: ChainHash::using_genesis_block(Network::Testnet),
+                               short_channel_id: 2,
+                               timestamp: 1,
+                               flags,
+                               cltv_expiry_delta: (2 << 4) | 0,
+                               htlc_minimum_msat: 0,
+                               htlc_maximum_msat: MAX_VALUE_MSAT,
+                               fee_base_msat: 0,
+                               fee_proportional_millionths: 0,
+                               excess_data: Vec::new()
+                       });
+               }
 
                add_channel(&gossip_sync, &secp_ctx, &privkeys[4], &privkeys[6], ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 1);
-               update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate {
-                       chain_hash: ChainHash::using_genesis_block(Network::Testnet),
-                       short_channel_id: 1,
-                       timestamp: 1,
-                       flags: 0,
-                       cltv_expiry_delta: (1 << 4) | 0,
-                       htlc_minimum_msat: 100,
-                       htlc_maximum_msat: MAX_VALUE_MSAT,
-                       fee_base_msat: 0,
-                       fee_proportional_millionths: 0,
-                       excess_data: Vec::new()
-               });
+               for (key, flags) in [(&privkeys[4], 0), (&privkeys[6], 3)] {
+                       update_channel(&gossip_sync, &secp_ctx, key, UnsignedChannelUpdate {
+                               chain_hash: ChainHash::using_genesis_block(Network::Testnet),
+                               short_channel_id: 1,
+                               timestamp: 1,
+                               flags,
+                               cltv_expiry_delta: (1 << 4) | 0,
+                               htlc_minimum_msat: 100,
+                               htlc_maximum_msat: MAX_VALUE_MSAT,
+                               fee_base_msat: 0,
+                               fee_proportional_millionths: 0,
+                               excess_data: Vec::new()
+                       });
+               }
                add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[6], NodeFeatures::from_le_bytes(id_to_feature_flags(6)), 0);
 
                {
@@ -7195,10 +7343,10 @@ mod tests {
        #[test]
        #[cfg(feature = "std")]
        fn generate_large_mpp_routes() {
-               use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
+               use crate::routing::scoring::ProbabilisticScoringFeeParameters;
 
                let logger = ln_test_utils::TestLogger::new();
-               let (graph, scorer) = match super::bench_utils::read_graph_scorer(&logger) {
+               let (graph, mut scorer) = match super::bench_utils::read_graph_scorer(&logger) {
                        Ok(res) => res,
                        Err(e) => {
                                eprintln!("{}", e);
@@ -7207,7 +7355,6 @@ mod tests {
                };
 
                let params = ProbabilisticScoringFeeParameters::default();
-               let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &*graph, &logger);
                let features = channelmanager::provided_bolt11_invoice_features(&UserConfig::default());
 
                super::bench_utils::generate_test_routes(&graph, &mut scorer, &params, features, random_init_seed(), 1_000_000, 2);
@@ -7394,7 +7541,7 @@ mod tests {
 
                // Make sure this works for blinded route hints.
                let blinded_path = BlindedPath {
-                       introduction_node_id: intermed_node_id,
+                       introduction_node: IntroductionNode::NodeId(intermed_node_id),
                        blinding_point: ln_test_utils::pubkey(42),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: ln_test_utils::pubkey(42), encrypted_payload: vec![] },
@@ -7428,7 +7575,7 @@ mod tests {
        #[test]
        fn blinded_route_ser() {
                let blinded_path_1 = BlindedPath {
-                       introduction_node_id: ln_test_utils::pubkey(42),
+                       introduction_node: IntroductionNode::NodeId(ln_test_utils::pubkey(42)),
                        blinding_point: ln_test_utils::pubkey(43),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: ln_test_utils::pubkey(44), encrypted_payload: Vec::new() },
@@ -7436,7 +7583,7 @@ mod tests {
                        ],
                };
                let blinded_path_2 = BlindedPath {
-                       introduction_node_id: ln_test_utils::pubkey(46),
+                       introduction_node: IntroductionNode::NodeId(ln_test_utils::pubkey(46)),
                        blinding_point: ln_test_utils::pubkey(47),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: ln_test_utils::pubkey(48), encrypted_payload: Vec::new() },
@@ -7495,7 +7642,7 @@ mod tests {
                // account for the blinded tail's final amount_msat.
                let mut inflight_htlcs = InFlightHtlcs::new();
                let blinded_path = BlindedPath {
-                       introduction_node_id: ln_test_utils::pubkey(43),
+                       introduction_node: IntroductionNode::NodeId(ln_test_utils::pubkey(43)),
                        blinding_point: ln_test_utils::pubkey(48),
                        blinded_hops: vec![BlindedHop { blinded_node_id: ln_test_utils::pubkey(49), encrypted_payload: Vec::new() }],
                };
@@ -7510,7 +7657,7 @@ mod tests {
                                maybe_announced_channel: false,
                        },
                        RouteHop {
-                               pubkey: blinded_path.introduction_node_id,
+                               pubkey: ln_test_utils::pubkey(43),
                                node_features: NodeFeatures::empty(),
                                short_channel_id: 43,
                                channel_features: ChannelFeatures::empty(),
@@ -7534,7 +7681,7 @@ mod tests {
        fn blinded_path_cltv_shadow_offset() {
                // Make sure we add a shadow offset when sending to blinded paths.
                let blinded_path = BlindedPath {
-                       introduction_node_id: ln_test_utils::pubkey(43),
+                       introduction_node: IntroductionNode::NodeId(ln_test_utils::pubkey(43)),
                        blinding_point: ln_test_utils::pubkey(44),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: ln_test_utils::pubkey(45), encrypted_payload: Vec::new() },
@@ -7552,7 +7699,7 @@ mod tests {
                                maybe_announced_channel: false,
                        },
                        RouteHop {
-                               pubkey: blinded_path.introduction_node_id,
+                               pubkey: ln_test_utils::pubkey(43),
                                node_features: NodeFeatures::empty(),
                                short_channel_id: 43,
                                channel_features: ChannelFeatures::empty(),
@@ -7594,7 +7741,7 @@ mod tests {
                let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                let mut blinded_path = BlindedPath {
-                       introduction_node_id: nodes[2],
+                       introduction_node: IntroductionNode::NodeId(nodes[2]),
                        blinding_point: ln_test_utils::pubkey(42),
                        blinded_hops: Vec::with_capacity(num_blinded_hops),
                };
@@ -7626,7 +7773,10 @@ mod tests {
                assert_eq!(tail.final_value_msat, 1001);
 
                let final_hop = route.paths[0].hops.last().unwrap();
-               assert_eq!(final_hop.pubkey, blinded_path.introduction_node_id);
+               assert_eq!(
+                       NodeId::from_pubkey(&final_hop.pubkey),
+                       *blinded_path.public_introduction_node_id(&network_graph).unwrap()
+               );
                if tail.hops.len() > 1 {
                        assert_eq!(final_hop.fee_msat,
                                blinded_payinfo.fee_base_msat as u64 + blinded_payinfo.fee_proportional_millionths as u64 * tail.final_value_msat / 1000000);
@@ -7649,7 +7799,7 @@ mod tests {
                let random_seed_bytes = keys_manager.get_secure_random_bytes();
 
                let mut invalid_blinded_path = BlindedPath {
-                       introduction_node_id: nodes[2],
+                       introduction_node: IntroductionNode::NodeId(nodes[2]),
                        blinding_point: ln_test_utils::pubkey(42),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: ln_test_utils::pubkey(43), encrypted_payload: vec![0; 43] },
@@ -7665,7 +7815,7 @@ mod tests {
                };
 
                let mut invalid_blinded_path_2 = invalid_blinded_path.clone();
-               invalid_blinded_path_2.introduction_node_id = ln_test_utils::pubkey(45);
+               invalid_blinded_path_2.introduction_node = IntroductionNode::NodeId(ln_test_utils::pubkey(45));
                let payment_params = PaymentParameters::blinded(vec![
                        (blinded_payinfo.clone(), invalid_blinded_path.clone()),
                        (blinded_payinfo.clone(), invalid_blinded_path_2)]);
@@ -7679,7 +7829,7 @@ mod tests {
                        _ => panic!("Expected error")
                }
 
-               invalid_blinded_path.introduction_node_id = our_id;
+               invalid_blinded_path.introduction_node = IntroductionNode::NodeId(our_id);
                let payment_params = PaymentParameters::blinded(vec![(blinded_payinfo.clone(), invalid_blinded_path.clone())]);
                let route_params = RouteParameters::from_payment_params_and_value(payment_params, 1001);
                match get_route(&our_id, &route_params, &network_graph, None, Arc::clone(&logger), &scorer,
@@ -7691,7 +7841,7 @@ mod tests {
                        _ => panic!("Expected error")
                }
 
-               invalid_blinded_path.introduction_node_id = ln_test_utils::pubkey(46);
+               invalid_blinded_path.introduction_node = IntroductionNode::NodeId(ln_test_utils::pubkey(46));
                invalid_blinded_path.blinded_hops.clear();
                let payment_params = PaymentParameters::blinded(vec![(blinded_payinfo, invalid_blinded_path)]);
                let route_params = RouteParameters::from_payment_params_and_value(payment_params, 1001);
@@ -7720,7 +7870,7 @@ mod tests {
 
                let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
                let blinded_path_1 = BlindedPath {
-                       introduction_node_id: nodes[2],
+                       introduction_node: IntroductionNode::NodeId(nodes[2]),
                        blinding_point: ln_test_utils::pubkey(42),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
@@ -7817,7 +7967,7 @@ mod tests {
                        get_channel_details(Some(1), nodes[1], InitFeatures::from_le_bytes(vec![0b11]), 10_000_000)];
 
                let blinded_path = BlindedPath {
-                       introduction_node_id: nodes[1],
+                       introduction_node: IntroductionNode::NodeId(nodes[1]),
                        blinding_point: ln_test_utils::pubkey(42),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
@@ -7886,7 +8036,7 @@ mod tests {
                                18446744073709551615)];
 
                let blinded_path = BlindedPath {
-                       introduction_node_id: nodes[1],
+                       introduction_node: IntroductionNode::NodeId(nodes[1]),
                        blinding_point: ln_test_utils::pubkey(42),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
@@ -7942,7 +8092,7 @@ mod tests {
                let amt_msat = 21_7020_5185_1423_0019;
 
                let blinded_path = BlindedPath {
-                       introduction_node_id: our_id,
+                       introduction_node: IntroductionNode::NodeId(our_id),
                        blinding_point: ln_test_utils::pubkey(42),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
@@ -7961,7 +8111,7 @@ mod tests {
                        (blinded_payinfo.clone(), blinded_path.clone()),
                        (blinded_payinfo.clone(), blinded_path.clone()),
                ];
-               blinded_hints[1].1.introduction_node_id = nodes[6];
+               blinded_hints[1].1.introduction_node = IntroductionNode::NodeId(nodes[6]);
 
                let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
                let payment_params = PaymentParameters::blinded(blinded_hints.clone())
@@ -7994,7 +8144,7 @@ mod tests {
                let amt_msat = 21_7020_5185_1423_0019;
 
                let blinded_path = BlindedPath {
-                       introduction_node_id: our_id,
+                       introduction_node: IntroductionNode::NodeId(our_id),
                        blinding_point: ln_test_utils::pubkey(42),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
@@ -8018,7 +8168,7 @@ mod tests {
                blinded_hints[1].0.htlc_minimum_msat = 21_7020_5185_1423_0019;
                blinded_hints[1].0.htlc_maximum_msat = 1844_6744_0737_0955_1615;
 
-               blinded_hints[2].1.introduction_node_id = nodes[6];
+               blinded_hints[2].1.introduction_node = IntroductionNode::NodeId(nodes[6]);
 
                let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
                let payment_params = PaymentParameters::blinded(blinded_hints.clone())
@@ -8065,7 +8215,7 @@ mod tests {
                let htlc_min = 2_5165_8240;
                let payment_params = if blinded_payee {
                        let blinded_path = BlindedPath {
-                               introduction_node_id: nodes[0],
+                               introduction_node: IntroductionNode::NodeId(nodes[0]),
                                blinding_point: ln_test_utils::pubkey(42),
                                blinded_hops: vec![
                                        BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
@@ -8145,7 +8295,7 @@ mod tests {
                let htlc_mins = [1_4392, 19_7401, 1027, 6_5535];
                let payment_params = if blinded_payee {
                        let blinded_path = BlindedPath {
-                               introduction_node_id: nodes[0],
+                               introduction_node: IntroductionNode::NodeId(nodes[0]),
                                blinding_point: ln_test_utils::pubkey(42),
                                blinded_hops: vec![
                                        BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
@@ -8246,7 +8396,7 @@ mod tests {
                                cltv_expiry_delta: 10,
                                features: BlindedHopFeatures::empty(),
                        }, BlindedPath {
-                               introduction_node_id: nodes[0],
+                               introduction_node: IntroductionNode::NodeId(nodes[0]),
                                blinding_point: ln_test_utils::pubkey(42),
                                blinded_hops: vec![
                                        BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
@@ -8296,7 +8446,7 @@ mod tests {
                let htlc_mins = [49_0000, 1125_0000];
                let payment_params = {
                        let blinded_path = BlindedPath {
-                               introduction_node_id: nodes[0],
+                               introduction_node: IntroductionNode::NodeId(nodes[0]),
                                blinding_point: ln_test_utils::pubkey(42),
                                blinded_hops: vec![
                                        BlindedHop { blinded_node_id: ln_test_utils::pubkey(42 as u8), encrypted_payload: Vec::new() },
@@ -8535,18 +8685,14 @@ pub(crate) mod bench_utils {
        use std::fs::File;
 
        use bitcoin::hashes::Hash;
-       use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
+       use bitcoin::secp256k1::SecretKey;
 
        use crate::chain::transaction::OutPoint;
-       use crate::routing::scoring::ScoreUpdate;
-       use crate::sign::{EntropySource, KeysManager};
-       use crate::ln::ChannelId;
-       use crate::ln::channelmanager::{self, ChannelCounterparty, ChannelDetails};
-       use crate::ln::features::Bolt11InvoiceFeatures;
-       use crate::routing::gossip::NetworkGraph;
-       use crate::routing::scoring::ProbabilisticScorer;
+       use crate::routing::scoring::{ProbabilisticScorer, ScoreUpdate};
+       use crate::sign::KeysManager;
+       use crate::ln::types::ChannelId;
+       use crate::ln::channelmanager::{self, ChannelCounterparty};
        use crate::util::config::UserConfig;
-       use crate::util::ser::ReadableArgs;
        use crate::util::test_utils::TestLogger;
        use crate::sync::Arc;
 
index cc6dee09cdfe2308ec52afd6d11415ccf2a57bf4..77e1a13065201bd9e705db19e795fb6640fe2fed 100644 (file)
@@ -64,7 +64,6 @@ use crate::util::logger::Logger;
 
 use crate::prelude::*;
 use core::{cmp, fmt};
-use core::convert::TryInto;
 use core::ops::{Deref, DerefMut};
 use core::time::Duration;
 use crate::io::{self, Read};
@@ -2153,7 +2152,7 @@ impl Readable for ChannelLiquidity {
 #[cfg(test)]
 mod tests {
        use super::{ChannelLiquidity, HistoricalBucketRangeTracker, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters, ProbabilisticScorer};
-       use crate::blinded_path::{BlindedHop, BlindedPath};
+       use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode};
        use crate::util::config::UserConfig;
 
        use crate::ln::channelmanager;
@@ -2167,7 +2166,7 @@ mod tests {
        use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::hashes::Hash;
        use bitcoin::hashes::sha256d::Hash as Sha256dHash;
-       use bitcoin::network::constants::Network;
+       use bitcoin::network::Network;
        use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
        use core::time::Duration;
        use crate::io;
@@ -3568,7 +3567,7 @@ mod tests {
                let mut path = payment_path_for_amount(768);
                let recipient_hop = path.hops.pop().unwrap();
                let blinded_path = BlindedPath {
-                       introduction_node_id: path.hops.last().as_ref().unwrap().pubkey,
+                       introduction_node: IntroductionNode::NodeId(path.hops.last().as_ref().unwrap().pubkey),
                        blinding_point: test_utils::pubkey(42),
                        blinded_hops: vec![
                                BlindedHop { blinded_node_id: test_utils::pubkey(44), encrypted_payload: Vec::new() }
index 3c0ef85fd7e3815696a669bc34203d411535f337..6337adfb81c09088a653102f7f01bc12c04daedc 100644 (file)
@@ -9,8 +9,7 @@
 
 use crate::routing::gossip::{NetworkGraph, NodeAlias, P2PGossipSync};
 use crate::ln::features::{ChannelFeatures, NodeFeatures};
-use crate::ln::msgs::{UnsignedChannelAnnouncement, ChannelAnnouncement, RoutingMessageHandler,
-       NodeAnnouncement, UnsignedNodeAnnouncement, ChannelUpdate, UnsignedChannelUpdate, MAX_VALUE_MSAT};
+use crate::ln::msgs::{ChannelAnnouncement, ChannelUpdate, MAX_VALUE_MSAT, NodeAnnouncement, RoutingMessageHandler, SocketAddress, UnsignedChannelAnnouncement, UnsignedChannelUpdate, UnsignedNodeAnnouncement};
 use crate::util::test_utils;
 use crate::util::ser::Writeable;
 
@@ -18,17 +17,18 @@ use bitcoin::blockdata::constants::ChainHash;
 use bitcoin::hashes::sha256d::Hash as Sha256dHash;
 use bitcoin::hashes::Hash;
 use bitcoin::hashes::hex::FromHex;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 use bitcoin::secp256k1::{PublicKey,SecretKey};
 use bitcoin::secp256k1::{Secp256k1, All};
 
+#[allow(unused)]
 use crate::prelude::*;
 use crate::sync::{self, Arc};
 
 use crate::routing::gossip::NodeId;
 
 // Using the same keys for LN and BTC ids
-pub(super) fn add_channel(
+pub(crate) fn add_channel(
        gossip_sync: &P2PGossipSync<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>,
        secp_ctx: &Secp256k1<All>, node_1_privkey: &SecretKey, node_2_privkey: &SecretKey, features: ChannelFeatures, short_channel_id: u64
 ) {
@@ -60,7 +60,7 @@ pub(super) fn add_channel(
        };
 }
 
-pub(super) fn add_or_update_node(
+pub(crate) fn add_or_update_node(
        gossip_sync: &P2PGossipSync<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>,
        secp_ctx: &Secp256k1<All>, node_privkey: &SecretKey, features: NodeFeatures, timestamp: u32
 ) {
@@ -71,7 +71,7 @@ pub(super) fn add_or_update_node(
                node_id,
                rgb: [0; 3],
                alias: NodeAlias([0; 32]),
-               addresses: Vec::new(),
+               addresses: vec![SocketAddress::TcpIpV4 { addr: [127, 0, 0, 1], port: 1000 }],
                excess_address_data: Vec::new(),
                excess_data: Vec::new(),
        };
@@ -87,7 +87,7 @@ pub(super) fn add_or_update_node(
        };
 }
 
-pub(super) fn update_channel(
+pub(crate) fn update_channel(
        gossip_sync: &P2PGossipSync<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>,
        secp_ctx: &Secp256k1<All>, node_privkey: &SecretKey, update: UnsignedChannelUpdate
 ) {
index ada90345ee6861ac53a7d81191ea04bfe93a508d..b4b8bf5144ee8c148305d1fd29ccc6346526805e 100644 (file)
@@ -14,6 +14,7 @@
 //! order to announce a channel. This module handles that checking.
 
 use bitcoin::TxOut;
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::constants::ChainHash;
 
 use hex::DisplayHex;
@@ -455,12 +456,12 @@ impl PendingChecks {
        pub(super) fn check_channel_announcement<U: Deref>(&self,
                utxo_lookup: &Option<U>, msg: &msgs::UnsignedChannelAnnouncement,
                full_msg: Option<&msgs::ChannelAnnouncement>
-       ) -> Result<Option<u64>, msgs::LightningError> where U::Target: UtxoLookup {
+       ) -> Result<Option<Amount>, msgs::LightningError> where U::Target: UtxoLookup {
                let handle_result = |res| {
                        match res {
                                Ok(TxOut { value, script_pubkey }) => {
                                        let expected_script =
-                                               make_funding_redeemscript_from_slices(msg.bitcoin_key_1.as_array(), msg.bitcoin_key_2.as_array()).to_v0_p2wsh();
+                                               make_funding_redeemscript_from_slices(msg.bitcoin_key_1.as_array(), msg.bitcoin_key_2.as_array()).to_p2wsh();
                                        if script_pubkey != expected_script {
                                                return Err(LightningError{
                                                        err: format!("Channel announcement key ({}) didn't match on-chain script ({})",
@@ -563,8 +564,8 @@ mod tests {
        use super::*;
        use crate::routing::gossip::tests::*;
        use crate::util::test_utils::{TestChainSource, TestLogger};
-       use crate::ln::msgs;
 
+       use bitcoin::amount::Amount;
        use bitcoin::secp256k1::{Secp256k1, SecretKey};
 
        use core::sync::atomic::Ordering;
@@ -611,7 +612,7 @@ mod tests {
 
                let future = UtxoFuture::new();
                future.resolve_without_forwarding(&network_graph,
-                       Ok(TxOut { value: 1_000_000, script_pubkey: good_script }));
+                       Ok(TxOut { value: Amount::from_sat(1_000_000), script_pubkey: good_script }));
                *chain_source.utxo_ret.lock().unwrap() = UtxoResult::Async(future.clone());
 
                network_graph.update_channel_from_announcement(&valid_announcement, &Some(&chain_source)).unwrap();
@@ -633,7 +634,7 @@ mod tests {
                assert!(network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).is_none());
 
                future.resolve_without_forwarding(&network_graph,
-                       Ok(TxOut { value: 0, script_pubkey: good_script }));
+                       Ok(TxOut { value: Amount::ZERO, script_pubkey: good_script }));
                network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).unwrap();
                network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).unwrap();
 
@@ -661,7 +662,7 @@ mod tests {
                assert!(network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).is_none());
 
                future.resolve_without_forwarding(&network_graph,
-                       Ok(TxOut { value: 1_000_000, script_pubkey: bitcoin::ScriptBuf::new() }));
+                       Ok(TxOut { value: Amount::from_sat(1_000_000), script_pubkey: bitcoin::ScriptBuf::new() }));
                assert!(network_graph.read_only().channels().get(&valid_announcement.contents.short_channel_id).is_none());
        }
 
@@ -710,7 +711,7 @@ mod tests {
                        "Awaiting channel_announcement validation to accept channel_update");
 
                future.resolve_without_forwarding(&network_graph,
-                       Ok(TxOut { value: 1_000_000, script_pubkey: good_script }));
+                       Ok(TxOut { value: Amount::from_sat(1_000_000), script_pubkey: good_script }));
 
                assert!(network_graph.read_only().channels()
                        .get(&valid_announcement.contents.short_channel_id).unwrap().one_to_two.is_some());
@@ -746,7 +747,7 @@ mod tests {
                        "Awaiting channel_announcement validation to accept channel_update");
 
                future.resolve_without_forwarding(&network_graph,
-                       Ok(TxOut { value: 1_000_000, script_pubkey: good_script }));
+                       Ok(TxOut { value: Amount::from_sat(1_000_000), script_pubkey: good_script }));
 
                assert_eq!(chan_update_a.contents.timestamp, chan_update_b.contents.timestamp);
                let graph_lock = network_graph.read_only();
@@ -793,7 +794,7 @@ mod tests {
 
                // Still, if we resolve the original future, the original channel will be accepted.
                future.resolve_without_forwarding(&network_graph,
-                       Ok(TxOut { value: 1_000_000, script_pubkey: good_script }));
+                       Ok(TxOut { value: Amount::from_sat(1_000_000), script_pubkey: good_script }));
                assert!(!network_graph.read_only().channels()
                        .get(&valid_announcement.contents.short_channel_id).unwrap()
                        .announcement_message.as_ref().unwrap()
index 070f48f122f9d6365bade472bb87fe3af8ee1b47..df2b40332c30fa8d27c8a9f853b936f461ec5bfd 100644 (file)
@@ -2,16 +2,19 @@
 
 use bitcoin::blockdata::transaction::Transaction;
 
-use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
-use bitcoin::secp256k1::ecdsa::Signature;
 use bitcoin::secp256k1;
+use bitcoin::secp256k1::ecdsa::Signature;
+use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
 
-use crate::util::ser::Writeable;
-use crate::ln::PaymentPreimage;
-use crate::ln::chan_utils::{HTLCOutputInCommitment, HolderCommitmentTransaction, CommitmentTransaction, ClosingTransaction};
+use crate::ln::chan_utils::{
+       ClosingTransaction, CommitmentTransaction, HTLCOutputInCommitment, HolderCommitmentTransaction,
+};
 use crate::ln::msgs::UnsignedChannelAnnouncement;
+use crate::ln::types::PaymentPreimage;
 
+#[allow(unused_imports)]
 use crate::prelude::*;
+
 use crate::sign::{ChannelSigner, HTLCDescriptor};
 
 /// A trait to sign Lightning channel transactions as described in
@@ -38,8 +41,8 @@ pub trait EcdsaChannelSigner: ChannelSigner {
        /// irrelevant or duplicate preimages.
        //
        // TODO: Document the things someone using this interface should enforce before signing.
-       fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction,
-               inbound_htlc_preimages: Vec<PaymentPreimage>,
+       fn sign_counterparty_commitment(
+               &self, commitment_tx: &CommitmentTransaction, inbound_htlc_preimages: Vec<PaymentPreimage>,
                outbound_htlc_preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>,
        ) -> Result<(Signature, Vec<Signature>), ()>;
        /// Creates a signature for a holder's commitment transaction.
@@ -60,15 +63,17 @@ pub trait EcdsaChannelSigner: ChannelSigner {
        /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked
        //
        // TODO: Document the things someone using this interface should enforce before signing.
-       fn sign_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction,
-               secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
+       fn sign_holder_commitment(
+               &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>,
+       ) -> Result<Signature, ()>;
        /// Same as [`sign_holder_commitment`], 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`] may enforce that we
        /// only ever get called once.
-       #[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
-       fn unsafe_sign_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction,
-               secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
+       #[cfg(any(test, feature = "unsafe_revoked_tx_signing"))]
+       fn unsafe_sign_holder_commitment(
+               &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>,
+       ) -> Result<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.
        ///
@@ -90,8 +95,9 @@ pub trait EcdsaChannelSigner: ChannelSigner {
        /// monitor.
        ///
        /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked
-       fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64,
-               per_commitment_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>
+       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.
@@ -118,9 +124,10 @@ pub trait EcdsaChannelSigner: ChannelSigner {
        /// monitor.
        ///
        /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked
-       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, ()>;
+       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, ()>;
        /// 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`].
@@ -137,8 +144,9 @@ pub trait EcdsaChannelSigner: ChannelSigner {
        /// [`EcdsaSighashType::All`]: bitcoin::sighash::EcdsaSighashType::All
        /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
        /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked
-       fn sign_holder_htlc_transaction(&self, htlc_tx: &Transaction, input: usize,
-               htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1<secp256k1::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.
@@ -164,15 +172,17 @@ pub trait EcdsaChannelSigner: ChannelSigner {
        /// monitor.
        ///
        /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked
-       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, ()>;
+       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, ()>;
+       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`.
        ///
@@ -197,15 +207,6 @@ pub trait EcdsaChannelSigner: ChannelSigner {
        ///
        /// [`NodeSigner::sign_gossip_message`]: crate::sign::NodeSigner::sign_gossip_message
        fn sign_channel_announcement_with_funding_key(
-               &self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>
+               &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 {}
index a7237493be7592676d0d22b2d6e00e43cf929995..885b8840b7650d9f4f49f811195cdef09c2822a9 100644 (file)
 //! The provided output descriptors follow a custom LDK data format and are currently not fully
 //! compatible with Bitcoin Core output descriptors.
 
+use bitcoin::amount::Amount;
+use bitcoin::bip32::{ChildNumber, Xpriv, Xpub};
 use bitcoin::blockdata::locktime::absolute::LockTime;
-use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn};
-use bitcoin::blockdata::script::{Script, ScriptBuf, Builder};
 use bitcoin::blockdata::opcodes;
+use bitcoin::blockdata::script::{Builder, Script, ScriptBuf};
+use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut};
 use bitcoin::ecdsa::Signature as EcdsaSignature;
-use bitcoin::network::constants::Network;
-use bitcoin::psbt::PartiallySignedTransaction;
-use bitcoin::bip32::{ExtendedPrivKey, ExtendedPubKey, ChildNumber};
+use bitcoin::network::Network;
 use bitcoin::sighash;
 use bitcoin::sighash::EcdsaSighashType;
+use bitcoin::transaction::Version;
 
-use bitcoin::bech32::u5;
-use bitcoin::hashes::{Hash, HashEngine};
+use bech32::u5;
 use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::hashes::sha256d::Hash as Sha256dHash;
-use bitcoin::hash_types::WPubkeyHash;
+use bitcoin::hashes::{Hash, HashEngine};
 
-#[cfg(taproot)]
-use bitcoin::secp256k1::All;
-use bitcoin::secp256k1::{KeyPair, PublicKey, Scalar, Secp256k1, SecretKey, Signing};
 use bitcoin::secp256k1::ecdh::SharedSecret;
 use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
 use bitcoin::secp256k1::schnorr;
-use bitcoin::{secp256k1, Sequence, Witness, Txid};
+#[cfg(taproot)]
+use bitcoin::secp256k1::All;
+use bitcoin::secp256k1::{Keypair, PublicKey, Scalar, Secp256k1, SecretKey, Signing};
+use bitcoin::{secp256k1, Psbt, Sequence, Txid, WPubkeyHash, Witness};
 
-use crate::util::transaction_utils;
-use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
-use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs};
 use crate::chain::transaction::OutPoint;
+use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
+use crate::ln::chan_utils;
+use crate::ln::chan_utils::{
+       get_revokeable_redeemscript, make_funding_redeemscript, ChannelPublicKeys,
+       ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction,
+       HTLCOutputInCommitment, HolderCommitmentTransaction,
+};
 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::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcKey, HtlcBasepoint, RevocationKey, RevocationBasepoint};
-use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
+use crate::ln::channel_keys::{
+       add_public_key_tweak, DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, HtlcKey,
+       RevocationBasepoint, RevocationKey,
+};
 #[cfg(taproot)]
 use crate::ln::msgs::PartialSignatureWithNonce;
+use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage};
 use crate::ln::script::ShutdownScript;
+use crate::ln::types::PaymentPreimage;
 use crate::offers::invoice::UnsignedBolt12Invoice;
 use crate::offers::invoice_request::UnsignedInvoiceRequest;
+use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
+use crate::util::transaction_utils;
 
-use crate::prelude::*;
-use core::convert::TryInto;
-use core::ops::Deref;
-use core::sync::atomic::{AtomicUsize, Ordering};
-#[cfg(taproot)]
-use musig2::types::{PartialSignature, PublicNonce};
+use crate::crypto::chacha20::ChaCha20;
 use crate::io::{self, Error};
 use crate::ln::features::ChannelTypeFeatures;
-use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
-use crate::sign::ecdsa::{EcdsaChannelSigner, WriteableEcdsaChannelSigner};
+use crate::ln::msgs::DecodeError;
+use crate::prelude::*;
+use crate::sign::ecdsa::EcdsaChannelSigner;
 #[cfg(taproot)]
 use crate::sign::taproot::TaprootChannelSigner;
 use crate::util::atomic_counter::AtomicCounter;
-use crate::crypto::chacha20::ChaCha20;
 use crate::util::invoice::construct_invoice_preimage;
+use core::convert::TryInto;
+use core::ops::Deref;
+use core::sync::atomic::{AtomicUsize, Ordering};
+#[cfg(taproot)]
+use musig2::types::{PartialSignature, PublicNonce};
 
 pub(crate) mod type_resolver;
 
@@ -103,14 +111,21 @@ pub struct DelayedPaymentOutputDescriptor {
        pub channel_keys_id: [u8; 32],
        /// The value of the channel which this output originated from, possibly indirectly.
        pub channel_value_satoshis: u64,
+       /// The channel public keys and other parameters needed to generate a spending transaction or
+       /// to provide to a re-derived signer through [`ChannelSigner::provide_channel_parameters`].
+       ///
+       /// Added as optional, but always `Some` if the descriptor was produced in v0.0.123 or later.
+       pub channel_transaction_parameters: Option<ChannelTransactionParameters>,
 }
+
 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: u64 = 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH as u64 + 1;
+       pub const MAX_WITNESS_LENGTH: u64 =
+               1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH as u64 + 1;
 }
 
 impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, {
@@ -121,6 +136,7 @@ impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, {
        (8, revocation_pubkey, required),
        (10, channel_keys_id, required),
        (12, channel_value_satoshis, required),
+       (13, channel_transaction_parameters, option),
 });
 
 pub(crate) const P2WPKH_WITNESS_WEIGHT: u64 = 1 /* num stack items */ +
@@ -129,6 +145,10 @@ pub(crate) const P2WPKH_WITNESS_WEIGHT: u64 = 1 /* num stack items */ +
        1 /* pubkey length */ +
        33 /* pubkey */;
 
+/// Witness weight for satisying a P2TR key-path spend.
+pub(crate) const P2TR_KEY_PATH_WITNESS_WEIGHT: u64 = 1 /* witness items */
+       + 1 /* schnorr sig len */ + 64 /* schnorr sig */;
+
 /// Information about a spendable output to our "payment key".
 ///
 /// See [`SpendableOutputDescriptor::StaticPaymentOutput`] for more details on how to spend this.
@@ -149,31 +169,28 @@ pub struct StaticPaymentOutputDescriptor {
        /// Added as optional, but always `Some` if the descriptor was produced in v0.0.117 or later.
        pub channel_transaction_parameters: Option<ChannelTransactionParameters>,
 }
+
 impl StaticPaymentOutputDescriptor {
        /// Returns the `witness_script` of the spendable output.
        ///
        /// Note that this will only return `Some` for [`StaticPaymentOutputDescriptor`]s that
        /// originated from an anchor outputs channel, as they take the form of a P2WSH script.
        pub fn witness_script(&self) -> Option<ScriptBuf> {
-               self.channel_transaction_parameters.as_ref()
-                       .and_then(|channel_params|
-                                if channel_params.channel_type_features.supports_anchors_zero_fee_htlc_tx() {
-                                       let payment_point = channel_params.holder_pubkeys.payment_point;
-                                       Some(chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point))
-                                } else {
-                                        None
-                                }
-                       )
+               self.channel_transaction_parameters.as_ref().and_then(|channel_params| {
+                       if channel_params.supports_anchors() {
+                               let payment_point = channel_params.holder_pubkeys.payment_point;
+                               Some(chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point))
+                       } else {
+                               None
+                       }
+               })
        }
 
        /// 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.
        pub fn max_witness_length(&self) -> u64 {
-               if self.channel_transaction_parameters.as_ref()
-                       .map(|channel_params| channel_params.channel_type_features.supports_anchors_zero_fee_htlc_tx())
-                       .unwrap_or(false)
-               {
+               if self.channel_transaction_parameters.as_ref().map_or(false, |p| p.supports_anchors()) {
                        let witness_script_weight = 1 /* pubkey push */ + 33 /* pubkey */ +
                                1 /* OP_CHECKSIGVERIFY */ + 1 /* OP_1 */ + 1 /* OP_CHECKSEQUENCEVERIFY */;
                        1 /* num witness items */ + 1 /* sig push */ + 73 /* sig including sighash flag */ +
@@ -223,7 +240,7 @@ pub enum SpendableOutputDescriptor {
                ///
                /// For channels which were generated prior to LDK 0.0.119, no such argument existed,
                /// however this field may still be filled in if such data is available.
-               channel_keys_id: Option<[u8; 32]>
+               channel_keys_id: Option<[u8; 32]>,
        },
        /// An output to a P2WSH script which can be spent with a single signature after an `OP_CSV`
        /// delay.
@@ -297,40 +314,116 @@ impl_writeable_tlv_based_enum!(SpendableOutputDescriptor,
 
 impl SpendableOutputDescriptor {
        /// Turns this into a [`bitcoin::psbt::Input`] which can be used to create a
-       /// [`PartiallySignedTransaction`] which spends the given descriptor.
+       /// [`Psbt`] which spends the given descriptor.
        ///
        /// Note that this does not include any signatures, just the information required to
        /// construct the transaction and sign it.
        ///
        /// This is not exported to bindings users as there is no standard serialization for an input.
        /// See [`Self::create_spendable_outputs_psbt`] instead.
-       pub fn to_psbt_input(&self) -> bitcoin::psbt::Input {
+       ///
+       /// The proprietary field is used to store add tweak for the signing key of this transaction.
+       /// See the [`DelayedPaymentBasepoint::derive_add_tweak`] docs for more info on add tweak and how to use it.
+       ///
+       /// To get the proprietary field use:
+       /// ```
+       /// use bitcoin::psbt::{Psbt};
+       /// use bitcoin::hashes::hex::FromHex;
+       ///
+       /// # let s = "70736274ff0100520200000001dee978529ab3e61a2987bea5183713d0e6d5ceb5ac81100fdb54a1a2\
+       ///     #                69cef505000000000090000000011f26000000000000160014abb3ab63280d4ccc5c11d6b50fd427a8\
+       ///     #                e19d6470000000000001012b10270000000000002200200afe4736760d814a2651bae63b572d935d9a\
+       /// #            b74a1a16c01774e341a32afa763601054d63210394a27a700617f5b7aee72bd4f8076b5770a582b7fb\
+       ///     #                d1d4ee2ea3802cd3cfbe2067029000b27521034629b1c8fdebfaeb58a74cd181f485e2c462e594cb30\
+       ///     #                34dee655875f69f6c7c968ac20fc144c444b5f7370656e6461626c655f6f7574707574006164645f74\
+       ///     #                7765616b20a86534f38ad61dc580ef41c3886204adf0911b81619c1ad7a2f5b5de39a2ba600000";
+       /// # let psbt = Psbt::deserialize(<Vec<u8> as FromHex>::from_hex(s).unwrap().as_slice()).unwrap();
+       /// let key = bitcoin::psbt::raw::ProprietaryKey {
+       ///     prefix: "LDK_spendable_output".as_bytes().to_vec(),
+       ///     subtype: 0,
+       ///     key: "add_tweak".as_bytes().to_vec(),
+       /// };
+       /// let value = psbt
+       ///     .inputs
+       ///     .first()
+       ///     .expect("Unable to get add tweak as there are no inputs")
+       ///     .proprietary
+       ///     .get(&key)
+       ///     .map(|x| x.to_owned());
+       /// ```
+       pub fn to_psbt_input<T: secp256k1::Signing>(
+               &self, secp_ctx: &Secp256k1<T>,
+       ) -> 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()
-                               }
+                               bitcoin::psbt::Input { witness_utxo: Some(output.clone()), ..Default::default() }
                        },
-                       SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
-                               // TODO we could add the witness script as well
+                       SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
+                               channel_transaction_parameters,
+                               per_commitment_point,
+                               revocation_pubkey,
+                               to_self_delay,
+                               output,
+                               ..
+                       }) => {
+                               let delayed_payment_basepoint = channel_transaction_parameters
+                                       .as_ref()
+                                       .map(|params| params.holder_pubkeys.delayed_payment_basepoint);
+
+                               let (witness_script, add_tweak) =
+                                       if let Some(basepoint) = delayed_payment_basepoint.as_ref() {
+                                               // Required to derive signing key: privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)
+                                               let add_tweak = basepoint.derive_add_tweak(&per_commitment_point);
+                                               let payment_key = DelayedPaymentKey(add_public_key_tweak(
+                                                       secp_ctx,
+                                                       &basepoint.to_public_key(),
+                                                       &add_tweak,
+                                               ));
+
+                                               (
+                                                       Some(get_revokeable_redeemscript(
+                                                               &revocation_pubkey,
+                                                               *to_self_delay,
+                                                               &payment_key,
+                                                       )),
+                                                       Some(add_tweak),
+                                               )
+                                       } else {
+                                               (None, None)
+                                       };
+
                                bitcoin::psbt::Input {
-                                       witness_utxo: Some(descriptor.output.clone()),
+                                       witness_utxo: Some(output.clone()),
+                                       witness_script,
+                                       proprietary: add_tweak
+                                               .map(|add_tweak| {
+                                                       [(
+                                                               bitcoin::psbt::raw::ProprietaryKey {
+                                                                       // A non standard namespace for spendable outputs, used to store the tweak needed
+                                                                       // to derive the private key
+                                                                       prefix: "LDK_spendable_output".as_bytes().to_vec(),
+                                                                       subtype: 0,
+                                                                       key: "add_tweak".as_bytes().to_vec(),
+                                                               },
+                                                               add_tweak.as_byte_array().to_vec(),
+                                                       )]
+                                                       .into_iter()
+                                                       .collect()
+                                               })
+                                               .unwrap_or_default(),
                                        ..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()
-                               }
+                       SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => bitcoin::psbt::Input {
+                               witness_utxo: Some(descriptor.output.clone()),
+                               witness_script: descriptor.witness_script(),
+                               ..Default::default()
                        },
                }
        }
 
-       /// Creates an unsigned [`PartiallySignedTransaction`] which spends the given descriptors to
+       /// Creates an unsigned [`Psbt`] 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.
        ///
@@ -346,24 +439,30 @@ impl SpendableOutputDescriptor {
        /// 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: ScriptBuf, feerate_sat_per_1000_weight: u32, locktime: Option<LockTime>) -> Result<(PartiallySignedTransaction, u64), ()> {
+       pub fn create_spendable_outputs_psbt<T: secp256k1::Signing>(
+               secp_ctx: &Secp256k1<T>, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
+               change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
+               locktime: Option<LockTime>,
+       ) -> Result<(Psbt, u64), ()> {
                let mut input = Vec::with_capacity(descriptors.len());
-               let mut input_value = 0;
+               let mut input_value = Amount::ZERO;
                let mut witness_weight = 0;
                let mut output_set = hash_set_with_capacity(descriptors.len());
                for outp in descriptors {
                        match outp {
                                SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
-                                       if !output_set.insert(descriptor.outpoint) { return Err(()); }
-                                       let sequence =
-                                               if descriptor.channel_transaction_parameters.as_ref()
-                                                       .map(|channel_params| channel_params.channel_type_features.supports_anchors_zero_fee_htlc_tx())
-                                                       .unwrap_or(false)
-                                               {
-                                                       Sequence::from_consensus(1)
-                                               } else {
-                                                       Sequence::ZERO
-                                               };
+                                       if !output_set.insert(descriptor.outpoint) {
+                                               return Err(());
+                                       }
+                                       let sequence = if descriptor
+                                               .channel_transaction_parameters
+                                               .as_ref()
+                                               .map_or(false, |p| p.supports_anchors())
+                                       {
+                                               Sequence::from_consensus(1)
+                                       } else {
+                                               Sequence::ZERO
+                                       };
                                        input.push(TxIn {
                                                previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
                                                script_sig: ScriptBuf::new(),
@@ -372,11 +471,16 @@ impl SpendableOutputDescriptor {
                                        });
                                        witness_weight += descriptor.max_witness_length();
                                        #[cfg(feature = "grind_signatures")]
-                                       { witness_weight -= 1; } // Guarantees a low R signature
+                                       {
+                                               // Guarantees a low R signature
+                                               witness_weight -= 1;
+                                       }
                                        input_value += descriptor.output.value;
                                },
                                SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
-                                       if !output_set.insert(descriptor.outpoint) { return Err(()); }
+                                       if !output_set.insert(descriptor.outpoint) {
+                                               return Err(());
+                                       }
                                        input.push(TxIn {
                                                previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
                                                script_sig: ScriptBuf::new(),
@@ -385,11 +489,16 @@ impl SpendableOutputDescriptor {
                                        });
                                        witness_weight += DelayedPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
                                        #[cfg(feature = "grind_signatures")]
-                                       { witness_weight -= 1; } // Guarantees a low R signature
+                                       {
+                                               // Guarantees a low R signature
+                                               witness_weight -= 1;
+                                       }
                                        input_value += descriptor.output.value;
                                },
                                SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output, .. } => {
-                                       if !output_set.insert(*outpoint) { return Err(()); }
+                                       if !output_set.insert(*outpoint) {
+                                               return Err(());
+                                       }
                                        input.push(TxIn {
                                                previous_output: outpoint.into_bitcoin_outpoint(),
                                                script_sig: ScriptBuf::new(),
@@ -398,23 +507,34 @@ impl SpendableOutputDescriptor {
                                        });
                                        witness_weight += 1 + 73 + 34;
                                        #[cfg(feature = "grind_signatures")]
-                                       { witness_weight -= 1; } // Guarantees a low R signature
+                                       {
+                                               // Guarantees a low R signature
+                                               witness_weight -= 1;
+                                       }
                                        input_value += output.value;
-                               }
+                               },
+                       }
+                       if input_value > Amount::MAX_MONEY {
+                               return Err(());
                        }
-                       if input_value > MAX_VALUE_MSAT / 1000 { return Err(()); }
                }
                let mut tx = Transaction {
-                       version: 2,
+                       version: Version::TWO,
                        lock_time: locktime.unwrap_or(LockTime::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 {
+               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(&secp_ctx)).collect::<Vec<_>>();
+               let psbt = Psbt {
                        inputs: psbt_inputs,
                        outputs: vec![Default::default(); tx.output.len()],
                        unsigned_tx: tx,
@@ -440,9 +560,9 @@ pub struct ChannelDerivationParameters {
 }
 
 impl_writeable_tlv_based!(ChannelDerivationParameters, {
-    (0, value_satoshis, required),
-    (2, keys_id, required),
-    (4, transaction_parameters, required),
+       (0, value_satoshis, required),
+       (2, keys_id, required),
+       (4, transaction_parameters, required),
 });
 
 /// A descriptor used to sign for a commitment transaction's HTLC output.
@@ -470,7 +590,7 @@ pub struct HTLCDescriptor {
        /// taken.
        pub preimage: Option<PaymentPreimage>,
        /// The counterparty's signature required to spend the HTLC output.
-       pub counterparty_sig: Signature
+       pub counterparty_sig: Signature,
 }
 
 impl_writeable_tlv_based!(HTLCDescriptor, {
@@ -496,10 +616,12 @@ impl HTLCDescriptor {
 
        /// Returns the UTXO to be spent by the HTLC input, which can be obtained via
        /// [`Self::unsigned_tx_input`].
-       pub fn previous_utxo<C: secp256k1::Signing + secp256k1::Verification>(&self, secp: &Secp256k1<C>) -> TxOut {
+       pub fn previous_utxo<C: secp256k1::Signing + secp256k1::Verification>(
+               &self, secp: &Secp256k1<C>,
+       ) -> TxOut {
                TxOut {
-                       script_pubkey: self.witness_script(secp).to_v0_p2wsh(),
-                       value: self.htlc.amount_msat / 1000,
+                       script_pubkey: self.witness_script(secp).to_p2wsh(),
+                       value: self.htlc.to_bitcoin_amount(),
                }
        }
 
@@ -507,40 +629,69 @@ impl HTLCDescriptor {
        /// transaction.
        pub fn unsigned_tx_input(&self) -> TxIn {
                chan_utils::build_htlc_input(
-                       &self.commitment_txid, &self.htlc, &self.channel_derivation_parameters.transaction_parameters.channel_type_features
+                       &self.commitment_txid,
+                       &self.htlc,
+                       &self.channel_derivation_parameters.transaction_parameters.channel_type_features,
                )
        }
 
        /// Returns the delayed output created as a result of spending the HTLC output in the commitment
        /// transaction.
-       pub fn tx_output<C: secp256k1::Signing + secp256k1::Verification>(&self, secp: &Secp256k1<C>) -> TxOut {
-               let channel_params = self.channel_derivation_parameters.transaction_parameters.as_holder_broadcastable();
+       pub fn tx_output<C: secp256k1::Signing + secp256k1::Verification>(
+               &self, secp: &Secp256k1<C>,
+       ) -> TxOut {
+               let channel_params =
+                       self.channel_derivation_parameters.transaction_parameters.as_holder_broadcastable();
                let broadcaster_keys = channel_params.broadcaster_pubkeys();
                let counterparty_keys = channel_params.countersignatory_pubkeys();
                let broadcaster_delayed_key = DelayedPaymentKey::from_basepoint(
-                       secp, &broadcaster_keys.delayed_payment_basepoint, &self.per_commitment_point
+                       secp,
+                       &broadcaster_keys.delayed_payment_basepoint,
+                       &self.per_commitment_point,
+               );
+               let counterparty_revocation_key = &RevocationKey::from_basepoint(
+                       &secp,
+                       &counterparty_keys.revocation_basepoint,
+                       &self.per_commitment_point,
                );
-               let counterparty_revocation_key = &RevocationKey::from_basepoint(&secp, &counterparty_keys.revocation_basepoint, &self.per_commitment_point);
                chan_utils::build_htlc_output(
-                       self.feerate_per_kw, channel_params.contest_delay(), &self.htlc,
-                       channel_params.channel_type_features(), &broadcaster_delayed_key, &counterparty_revocation_key
+                       self.feerate_per_kw,
+                       channel_params.contest_delay(),
+                       &self.htlc,
+                       channel_params.channel_type_features(),
+                       &broadcaster_delayed_key,
+                       &counterparty_revocation_key,
                )
        }
 
        /// Returns the witness script of the HTLC output in the commitment transaction.
-       pub fn witness_script<C: secp256k1::Signing + secp256k1::Verification>(&self, secp: &Secp256k1<C>) -> ScriptBuf {
-               let channel_params = self.channel_derivation_parameters.transaction_parameters.as_holder_broadcastable();
+       pub fn witness_script<C: secp256k1::Signing + secp256k1::Verification>(
+               &self, secp: &Secp256k1<C>,
+       ) -> ScriptBuf {
+               let channel_params =
+                       self.channel_derivation_parameters.transaction_parameters.as_holder_broadcastable();
                let broadcaster_keys = channel_params.broadcaster_pubkeys();
                let counterparty_keys = channel_params.countersignatory_pubkeys();
                let broadcaster_htlc_key = HtlcKey::from_basepoint(
-                       secp, &broadcaster_keys.htlc_basepoint, &self.per_commitment_point
+                       secp,
+                       &broadcaster_keys.htlc_basepoint,
+                       &self.per_commitment_point,
                );
                let counterparty_htlc_key = HtlcKey::from_basepoint(
-                       secp, &counterparty_keys.htlc_basepoint, &self.per_commitment_point,
+                       secp,
+                       &counterparty_keys.htlc_basepoint,
+                       &self.per_commitment_point,
+               );
+               let counterparty_revocation_key = &RevocationKey::from_basepoint(
+                       &secp,
+                       &counterparty_keys.revocation_basepoint,
+                       &self.per_commitment_point,
                );
-               let counterparty_revocation_key = &RevocationKey::from_basepoint(&secp, &counterparty_keys.revocation_basepoint, &self.per_commitment_point);
                chan_utils::get_htlc_redeemscript_with_explicit_keys(
-                       &self.htlc, channel_params.channel_type_features(), &broadcaster_htlc_key, &counterparty_htlc_key,
+                       &self.htlc,
+                       channel_params.channel_type_features(),
+                       &broadcaster_htlc_key,
+                       &counterparty_htlc_key,
                        &counterparty_revocation_key,
                )
        }
@@ -549,21 +700,25 @@ impl HTLCDescriptor {
        /// transaction.
        pub fn tx_input_witness(&self, signature: &Signature, witness_script: &Script) -> Witness {
                chan_utils::build_htlc_input_witness(
-                       signature, &self.counterparty_sig, &self.preimage, witness_script,
-                       &self.channel_derivation_parameters.transaction_parameters.channel_type_features
+                       signature,
+                       &self.counterparty_sig,
+                       &self.preimage,
+                       witness_script,
+                       &self.channel_derivation_parameters.transaction_parameters.channel_type_features,
                )
        }
 
        /// Derives the channel signer required to sign the HTLC input.
-       pub fn derive_channel_signer<S: WriteableEcdsaChannelSigner, SP: Deref>(&self, signer_provider: &SP) -> S
+       pub fn derive_channel_signer<S: EcdsaChannelSigner, SP: Deref>(&self, signer_provider: &SP) -> S
        where
-               SP::Target: SignerProvider<EcdsaSigner= S>
+               SP::Target: SignerProvider<EcdsaSigner = S>,
        {
                let mut signer = signer_provider.derive_channel_signer(
                        self.channel_derivation_parameters.value_satoshis,
                        self.channel_derivation_parameters.keys_id,
                );
-               signer.provide_channel_parameters(&self.channel_derivation_parameters.transaction_parameters);
+               signer
+                       .provide_channel_parameters(&self.channel_derivation_parameters.transaction_parameters);
                signer
        }
 }
@@ -574,7 +729,8 @@ 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;
+       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
        ///
@@ -600,8 +756,10 @@ pub trait ChannelSigner {
        ///
        /// 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,
-               outbound_htlc_preimages: Vec<PaymentPreimage>) -> Result<(), ()>;
+       fn validate_holder_commitment(
+               &self, holder_tx: &HolderCommitmentTransaction,
+               outbound_htlc_preimages: Vec<PaymentPreimage>,
+       ) -> Result<(), ()>;
 
        /// Validate the counterparty's revocation.
        ///
@@ -682,7 +840,9 @@ pub trait NodeSigner {
        /// 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, ()>;
+       fn ecdh(
+               &self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>,
+       ) -> Result<SharedSecret, ()>;
 
        /// Sign an invoice.
        ///
@@ -695,7 +855,9 @@ pub trait NodeSigner {
        /// 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, ()>;
+       fn sign_invoice(
+               &self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient,
+       ) -> Result<RecoverableSignature, ()>;
 
        /// Signs the [`TaggedHash`] of a BOLT 12 invoice request.
        ///
@@ -709,7 +871,7 @@ pub trait NodeSigner {
        ///
        /// [`TaggedHash`]: crate::offers::merkle::TaggedHash
        fn sign_bolt12_invoice_request(
-               &self, invoice_request: &UnsignedInvoiceRequest
+               &self, invoice_request: &UnsignedInvoiceRequest,
        ) -> Result<schnorr::Signature, ()>;
 
        /// Signs the [`TaggedHash`] of a BOLT 12 invoice.
@@ -724,7 +886,7 @@ pub trait NodeSigner {
        ///
        /// [`TaggedHash`]: crate::offers::merkle::TaggedHash
        fn sign_bolt12_invoice(
-               &self, invoice: &UnsignedBolt12Invoice
+               &self, invoice: &UnsignedBolt12Invoice,
        ) -> Result<schnorr::Signature, ()>;
 
        /// Sign a gossip message.
@@ -736,10 +898,50 @@ pub trait NodeSigner {
        fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()>;
 }
 
+/// A trait that describes a wallet capable of creating a spending [`Transaction`] from a set of
+/// [`SpendableOutputDescriptor`]s.
+pub trait OutputSpender {
+       /// 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.
+       fn spend_spendable_outputs<C: Signing>(
+               &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
+               change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
+               locktime: Option<LockTime>, secp_ctx: &Secp256k1<C>,
+       ) -> Result<Transaction, ()>;
+}
+
+// Primarily needed in doctests because of https://github.com/rust-lang/rust/issues/67295
+/// A dynamic [`SignerProvider`] temporarily needed for doc tests.
+///
+/// This is not exported to bindings users as it is not intended for public consumption.
+#[cfg(taproot)]
+#[doc(hidden)]
+#[deprecated(note = "Remove once taproot cfg is removed")]
+pub type DynSignerProvider =
+       dyn SignerProvider<EcdsaSigner = InMemorySigner, TaprootSigner = InMemorySigner>;
+
+/// A dynamic [`SignerProvider`] temporarily needed for doc tests.
+///
+/// This is not exported to bindings users as it is not intended for public consumption.
+#[cfg(not(taproot))]
+#[doc(hidden)]
+#[deprecated(note = "Remove once taproot cfg is removed")]
+pub type DynSignerProvider = dyn SignerProvider<EcdsaSigner = InMemorySigner>;
+
 /// 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 EcdsaSigner: WriteableEcdsaChannelSigner;
+       /// A type which implements [`EcdsaChannelSigner`] which will be returned by [`Self::derive_channel_signer`].
+       type EcdsaSigner: EcdsaChannelSigner;
        #[cfg(taproot)]
        /// A type which implements [`TaprootChannelSigner`]
        type TaprootSigner: TaprootChannelSigner;
@@ -750,7 +952,9 @@ pub trait SignerProvider {
        /// `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];
+       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`.
        ///
@@ -758,11 +962,13 @@ pub trait SignerProvider {
        /// [`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::EcdsaSigner;
+       fn derive_channel_signer(
+               &self, channel_value_satoshis: u64, channel_keys_id: [u8; 32],
+       ) -> Self::EcdsaSigner;
 
        /// 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).
+       /// [`EcdsaChannelSigner`]-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.
@@ -795,7 +1001,18 @@ pub trait SignerProvider {
        fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()>;
 }
 
-/// A simple implementation of [`WriteableEcdsaChannelSigner`] that just keeps the private keys in memory.
+/// A helper trait that describes an on-chain wallet capable of returning a (change) destination
+/// script.
+pub trait ChangeDestinationSource {
+       /// Returns a script pubkey which can be used as a change destination for
+       /// [`OutputSpender::spend_spendable_outputs`].
+       ///
+       /// This method should return a different value each time it is called, to avoid linking
+       /// on-chain funds controlled to the same user.
+       fn get_change_destination_script(&self) -> Result<ScriptBuf, ()>;
+}
+
+/// A simple implementation of [`EcdsaChannelSigner`] that just keeps the private keys in memory.
 ///
 /// This implementation performs no policy checks and is insufficient by itself as
 /// a secure external signer.
@@ -828,16 +1045,16 @@ pub struct InMemorySigner {
 
 impl PartialEq for InMemorySigner {
        fn eq(&self, other: &Self) -> bool {
-               self.funding_key == other.funding_key &&
-                       self.revocation_base_key == other.revocation_base_key &&
-                       self.payment_key == other.payment_key &&
-                       self.delayed_payment_base_key == other.delayed_payment_base_key &&
-                       self.htlc_base_key == other.htlc_base_key &&
-                       self.commitment_seed == other.commitment_seed &&
-                       self.holder_channel_pubkeys == other.holder_channel_pubkeys &&
-                       self.channel_parameters == other.channel_parameters &&
-                       self.channel_value_satoshis == other.channel_value_satoshis &&
-                       self.channel_keys_id == other.channel_keys_id
+               self.funding_key == other.funding_key
+                       && self.revocation_base_key == other.revocation_base_key
+                       && self.payment_key == other.payment_key
+                       && self.delayed_payment_base_key == other.delayed_payment_base_key
+                       && self.htlc_base_key == other.htlc_base_key
+                       && self.commitment_seed == other.commitment_seed
+                       && self.holder_channel_pubkeys == other.holder_channel_pubkeys
+                       && self.channel_parameters == other.channel_parameters
+                       && self.channel_value_satoshis == other.channel_value_satoshis
+                       && self.channel_keys_id == other.channel_keys_id
        }
 }
 
@@ -862,21 +1079,19 @@ impl Clone for InMemorySigner {
 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],
+               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);
+               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,
@@ -892,18 +1107,18 @@ impl InMemorySigner {
                }
        }
 
-       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 {
+       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: RevocationBasepoint::from(from_secret(&revocation_base_key)),
                        payment_point: from_secret(&payment_key),
-                       delayed_payment_basepoint: DelayedPaymentBasepoint::from(from_secret(&delayed_payment_base_key)),
+                       delayed_payment_basepoint: DelayedPaymentBasepoint::from(from_secret(
+                               &delayed_payment_base_key,
+                       )),
                        htlc_basepoint: HtlcBasepoint::from(from_secret(&htlc_base_key)),
                }
        }
@@ -913,8 +1128,9 @@ impl InMemorySigner {
        /// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called.
        /// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation.
        pub fn counterparty_pubkeys(&self) -> Option<&ChannelPublicKeys> {
-               self.get_channel_parameters()
-                       .and_then(|params| params.counterparty_parameters.as_ref().map(|params| &params.pubkeys))
+               self.get_channel_parameters().and_then(|params| {
+                       params.counterparty_parameters.as_ref().map(|params| &params.pubkeys)
+               })
        }
 
        /// Returns the `contest_delay` value specified by our counterparty and applied on holder-broadcastable
@@ -924,8 +1140,9 @@ impl InMemorySigner {
        /// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called.
        /// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation.
        pub fn counterparty_selected_contest_delay(&self) -> Option<u16> {
-               self.get_channel_parameters()
-                       .and_then(|params| params.counterparty_parameters.as_ref().map(|params| params.selected_contest_delay))
+               self.get_channel_parameters().and_then(|params| {
+                       params.counterparty_parameters.as_ref().map(|params| params.selected_contest_delay)
+               })
        }
 
        /// Returns the `contest_delay` value specified by us and applied on transactions broadcastable
@@ -980,19 +1197,30 @@ impl InMemorySigner {
        /// 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<Witness, ()> {
+       pub fn sign_counterparty_payment_input<C: Signing>(
+               &self, spend_tx: &Transaction, input_idx: usize,
+               descriptor: &StaticPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>,
+       ) -> Result<Witness, ()> {
                // 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.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 = bitcoin::PublicKey::new(self.pubkeys().payment_point);
                // We cannot always assume that `channel_parameters` is set, so can't just call
                // `self.channel_parameters()` or anything that relies on it
-               let supports_anchors_zero_fee_htlc_tx = self.channel_type_features()
+               let supports_anchors_zero_fee_htlc_tx = self
+                       .channel_type_features()
                        .map(|features| features.supports_anchors_zero_fee_htlc_tx())
                        .unwrap_or(false);
 
@@ -1001,15 +1229,26 @@ impl InMemorySigner {
                } else {
                        ScriptBuf::new_p2pkh(&remotepubkey.pubkey_hash())
                };
-               let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
+               let sighash = hash_to_message!(
+                       &sighash::SighashCache::new(spend_tx)
+                               .p2wsh_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 = if supports_anchors_zero_fee_htlc_tx {
-                       witness_script.to_v0_p2wsh()
+                       witness_script.to_p2wsh()
                } else {
-                       ScriptBuf::new_v0_p2wpkh(&remotepubkey.wpubkey_hash().unwrap())
+                       ScriptBuf::new_p2wpkh(&remotepubkey.wpubkey_hash().unwrap())
                };
 
-               if payment_script != descriptor.output.script_pubkey { return Err(()); }
+               if payment_script != descriptor.output.script_pubkey {
+                       return Err(());
+               }
 
                let mut witness = Vec::with_capacity(2);
                witness.push(remotesig.serialize_der().to_vec());
@@ -1032,27 +1271,60 @@ impl InMemorySigner {
        ///
        /// [`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<Witness, ()> {
+       pub fn sign_dynamic_p2wsh_input<C: Signing>(
+               &self, spend_tx: &Transaction, input_idx: usize,
+               descriptor: &DelayedPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>,
+       ) -> Result<Witness, ()> {
                // 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 = DelayedPaymentKey::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()[..]);
+               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 =
+                       DelayedPaymentKey::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)
+                               .p2wsh_signature_hash(
+                                       input_idx,
+                                       &witness_script,
+                                       descriptor.output.value,
+                                       EcdsaSighashType::All
+                               )
+                               .unwrap()[..]
+               );
                let local_delayedsig = EcdsaSignature {
                        sig: sign_with_aux_rand(secp_ctx, &sighash, &delayed_payment_key, &self),
                        hash_ty: EcdsaSighashType::All,
                };
-               let payment_script = bitcoin::Address::p2wsh(&witness_script, Network::Bitcoin).script_pubkey();
+               let payment_script =
+                       bitcoin::Address::p2wsh(&witness_script, Network::Bitcoin).script_pubkey();
 
-               if descriptor.output.script_pubkey != payment_script { return Err(()); }
+               if descriptor.output.script_pubkey != payment_script {
+                       return Err(());
+               }
 
                Ok(Witness::from_slice(&[
                        &local_delayedsig.serialize()[..],
@@ -1069,8 +1341,12 @@ impl EntropySource for InMemorySigner {
 }
 
 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();
+       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)
        }
 
@@ -1078,7 +1354,10 @@ impl ChannelSigner for InMemorySigner {
                chan_utils::build_commitment_secret(&self.commitment_seed, idx)
        }
 
-       fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction, _outbound_htlc_preimages: Vec<PaymentPreimage>) -> Result<(), ()> {
+       fn validate_holder_commitment(
+               &self, _holder_tx: &HolderCommitmentTransaction,
+               _outbound_htlc_preimages: Vec<PaymentPreimage>,
+       ) -> Result<(), ()> {
                Ok(())
        }
 
@@ -1086,12 +1365,19 @@ impl ChannelSigner for InMemorySigner {
                Ok(())
        }
 
-       fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys }
+       fn pubkeys(&self) -> &ChannelPublicKeys {
+               &self.holder_channel_pubkeys
+       }
 
-       fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }
+       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);
+               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;
@@ -1101,19 +1387,30 @@ impl ChannelSigner for InMemorySigner {
        }
 }
 
-const MISSING_PARAMS_ERR: &'static str = "ChannelSigner::provide_channel_parameters must be called before signing operations";
+const MISSING_PARAMS_ERR: &'static str =
+       "ChannelSigner::provide_channel_parameters must be called before signing operations";
 
 impl EcdsaChannelSigner for InMemorySigner {
-       fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _inbound_htlc_preimages: Vec<PaymentPreimage>, _outbound_htlc_preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
+       fn sign_counterparty_commitment(
+               &self, commitment_tx: &CommitmentTransaction,
+               _inbound_htlc_preimages: Vec<PaymentPreimage>,
+               _outbound_htlc_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 counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR);
-               let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey);
+               let channel_funding_redeemscript =
+                       make_funding_redeemscript(&funding_pubkey, &counterparty_keys.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_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());
@@ -1122,124 +1419,268 @@ impl EcdsaChannelSigner for InMemorySigner {
                        let holder_selected_contest_delay =
                                self.holder_selected_contest_delay().expect(MISSING_PARAMS_ERR);
                        let chan_type = &channel_parameters.channel_type_features;
-                       let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_tx.feerate_per_kw(), holder_selected_contest_delay, htlc, chan_type, &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
+                       let htlc_tx = chan_utils::build_htlc_transaction(
+                               &commitment_txid,
+                               commitment_tx.feerate_per_kw(),
+                               holder_selected_contest_delay,
+                               htlc,
+                               chan_type,
+                               &keys.broadcaster_delayed_payment_key,
+                               &keys.revocation_key,
+                       );
                        let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, chan_type, &keys);
-                       let htlc_sighashtype = if chan_type.supports_anchors_zero_fee_htlc_tx() { 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);
+                       let htlc_sighashtype = if chan_type.supports_anchors_zero_fee_htlc_tx() {
+                               EcdsaSighashType::SinglePlusAnyoneCanPay
+                       } else {
+                               EcdsaSighashType::All
+                       };
+                       let htlc_sighash = hash_to_message!(
+                               &sighash::SighashCache::new(&htlc_tx)
+                                       .p2wsh_signature_hash(
+                                               0,
+                                               &htlc_redeemscript,
+                                               htlc.to_bitcoin_amount(),
+                                               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 sign_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
+       fn sign_holder_commitment(
+               &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>,
+       ) -> Result<Signature, ()> {
                let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
                let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR);
-               let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey);
+               let funding_redeemscript =
+                       make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey);
                let trusted_tx = commitment_tx.trust();
-               Ok(trusted_tx.built_transaction().sign_holder_commitment(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, &self, secp_ctx))
-       }
-
-       #[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
-       fn unsafe_sign_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
+               Ok(trusted_tx.built_transaction().sign_holder_commitment(
+                       &self.funding_key,
+                       &funding_redeemscript,
+                       self.channel_value_satoshis,
+                       &self,
+                       secp_ctx,
+               ))
+       }
+
+       #[cfg(any(test, feature = "unsafe_revoked_tx_signing"))]
+       fn unsafe_sign_holder_commitment(
+               &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>,
+       ) -> Result<Signature, ()> {
                let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
                let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR);
-               let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey);
+               let funding_redeemscript =
+                       make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey);
                let trusted_tx = commitment_tx.trust();
-               Ok(trusted_tx.built_transaction().sign_holder_commitment(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, &self, secp_ctx))
-       }
-
-       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);
+               Ok(trusted_tx.built_transaction().sign_holder_commitment(
+                       &self.funding_key,
+                       &funding_redeemscript,
+                       self.channel_value_satoshis,
+                       &self,
+                       secp_ctx,
+               ))
+       }
+
+       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 = RevocationKey::from_basepoint(
-                       &secp_ctx,  &self.pubkeys().revocation_basepoint, &per_commitment_point,
+                       &secp_ctx,
+                       &self.pubkeys().revocation_basepoint,
+                       &per_commitment_point,
                );
                let witness_script = {
                        let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR);
                        let holder_selected_contest_delay =
                                self.holder_selected_contest_delay().expect(MISSING_PARAMS_ERR);
-                       let counterparty_delayedpubkey = DelayedPaymentKey::from_basepoint(&secp_ctx, &counterparty_keys.delayed_payment_basepoint, &per_commitment_point);
-                       chan_utils::get_revokeable_redeemscript(&revocation_pubkey, holder_selected_contest_delay, &counterparty_delayedpubkey)
+                       let counterparty_delayedpubkey = DelayedPaymentKey::from_basepoint(
+                               &secp_ctx,
+                               &counterparty_keys.delayed_payment_basepoint,
+                               &per_commitment_point,
+                       );
+                       chan_utils::get_revokeable_redeemscript(
+                               &revocation_pubkey,
+                               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))
+               let sighash = hash_to_message!(
+                       &sighash_parts
+                               .p2wsh_signature_hash(
+                                       input,
+                                       &witness_script,
+                                       Amount::from_sat(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);
+       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 = RevocationKey::from_basepoint(
-                       &secp_ctx,  &self.pubkeys().revocation_basepoint, &per_commitment_point,
+                       &secp_ctx,
+                       &self.pubkeys().revocation_basepoint,
+                       &per_commitment_point,
                );
                let witness_script = {
                        let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR);
                        let counterparty_htlcpubkey = HtlcKey::from_basepoint(
-                               &secp_ctx, &counterparty_keys.htlc_basepoint, &per_commitment_point,
+                               &secp_ctx,
+                               &counterparty_keys.htlc_basepoint,
+                               &per_commitment_point,
                        );
                        let holder_htlcpubkey = HtlcKey::from_basepoint(
-                               &secp_ctx, &self.pubkeys().htlc_basepoint, &per_commitment_point,
+                               &secp_ctx,
+                               &self.pubkeys().htlc_basepoint,
+                               &per_commitment_point,
                        );
                        let chan_type = self.channel_type_features().expect(MISSING_PARAMS_ERR);
-                       chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, chan_type, &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey)
+                       chan_utils::get_htlc_redeemscript_with_explicit_keys(
+                               &htlc,
+                               chan_type,
+                               &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))
+               let sighash = hash_to_message!(
+                       &sighash_parts
+                               .p2wsh_signature_hash(
+                                       input,
+                                       &witness_script,
+                                       Amount::from_sat(amount),
+                                       EcdsaSighashType::All
+                               )
+                               .unwrap()[..]
+               );
+               return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self));
        }
 
        fn sign_holder_htlc_transaction(
                &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor,
-               secp_ctx: &Secp256k1<secp256k1::All>
+               secp_ctx: &Secp256k1<secp256k1::All>,
        ) -> Result<Signature, ()> {
                let witness_script = htlc_descriptor.witness_script(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 sighash = &sighash::SighashCache::new(&*htlc_tx)
+                       .p2wsh_signature_hash(
+                               input,
+                               &witness_script,
+                               htlc_descriptor.htlc.to_bitcoin_amount(),
+                               EcdsaSighashType::All,
+                       )
+                       .map_err(|_| ())?;
                let our_htlc_private_key = chan_utils::derive_private_key(
-                       &secp_ctx, &htlc_descriptor.per_commitment_point, &self.htlc_base_key
+                       &secp_ctx,
+                       &htlc_descriptor.per_commitment_point,
+                       &self.htlc_base_key,
                );
-               Ok(sign_with_aux_rand(&secp_ctx, &hash_to_message!(sighash.as_byte_array()), &our_htlc_private_key, &self))
+               let sighash = hash_to_message!(sighash.as_byte_array());
+               Ok(sign_with_aux_rand(&secp_ctx, &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);
+       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 = RevocationKey::from_basepoint(
-                       &secp_ctx,  &self.pubkeys().revocation_basepoint, &per_commitment_point,
+                       &secp_ctx,
+                       &self.pubkeys().revocation_basepoint,
+                       &per_commitment_point,
                );
                let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR);
                let counterparty_htlcpubkey = HtlcKey::from_basepoint(
-                       &secp_ctx, &counterparty_keys.htlc_basepoint, &per_commitment_point,
+                       &secp_ctx,
+                       &counterparty_keys.htlc_basepoint,
+                       &per_commitment_point,
                );
-               let htlcpubkey = HtlcKey::from_basepoint(&secp_ctx, &self.pubkeys().htlc_basepoint, &per_commitment_point);
+               let htlc_basepoint = self.pubkeys().htlc_basepoint;
+               let htlcpubkey = HtlcKey::from_basepoint(&secp_ctx, &htlc_basepoint, &per_commitment_point);
                let chan_type = self.channel_type_features().expect(MISSING_PARAMS_ERR);
-               let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, chan_type, &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey);
+               let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(
+                       &htlc,
+                       chan_type,
+                       &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()[..]);
+               let sighash = hash_to_message!(
+                       &sighash_parts
+                               .p2wsh_signature_hash(
+                                       input,
+                                       &witness_script,
+                                       Amount::from_sat(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, ()> {
+       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 counterparty_funding_key = &self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR).funding_pubkey;
-               let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, counterparty_funding_key);
-               Ok(closing_tx.trust().sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
+               let counterparty_funding_key =
+                       &self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR).funding_pubkey;
+               let channel_funding_redeemscript =
+                       make_funding_redeemscript(&funding_pubkey, counterparty_funding_key);
+               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();
+               let witness_script =
+                       chan_utils::get_anchor_redeemscript(&self.holder_channel_pubkeys.funding_pubkey);
+               let sighash = sighash::SighashCache::new(&*anchor_tx)
+                       .p2wsh_signature_hash(
+                               input,
+                               &witness_script,
+                               Amount::from_sat(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>
+               &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))
@@ -1248,39 +1689,64 @@ impl EcdsaChannelSigner for InMemorySigner {
 
 #[cfg(taproot)]
 impl TaprootChannelSigner for InMemorySigner {
-       fn generate_local_nonce_pair(&self, commitment_number: u64, secp_ctx: &Secp256k1<All>) -> PublicNonce {
+       fn generate_local_nonce_pair(
+               &self, commitment_number: u64, secp_ctx: &Secp256k1<All>,
+       ) -> PublicNonce {
                todo!()
        }
 
-       fn partially_sign_counterparty_commitment(&self, counterparty_nonce: PublicNonce, commitment_tx: &CommitmentTransaction, inbound_htlc_preimages: Vec<PaymentPreimage>, outbound_htlc_preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<All>) -> Result<(PartialSignatureWithNonce, Vec<schnorr::Signature>), ()> {
+       fn partially_sign_counterparty_commitment(
+               &self, counterparty_nonce: PublicNonce, commitment_tx: &CommitmentTransaction,
+               inbound_htlc_preimages: Vec<PaymentPreimage>,
+               outbound_htlc_preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<All>,
+       ) -> Result<(PartialSignatureWithNonce, Vec<schnorr::Signature>), ()> {
                todo!()
        }
 
-       fn finalize_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction, counterparty_partial_signature: PartialSignatureWithNonce, secp_ctx: &Secp256k1<All>) -> Result<PartialSignature, ()> {
+       fn finalize_holder_commitment(
+               &self, commitment_tx: &HolderCommitmentTransaction,
+               counterparty_partial_signature: PartialSignatureWithNonce, secp_ctx: &Secp256k1<All>,
+       ) -> Result<PartialSignature, ()> {
                todo!()
        }
 
-       fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1<All>) -> Result<schnorr::Signature, ()> {
+       fn sign_justice_revoked_output(
+               &self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey,
+               secp_ctx: &Secp256k1<All>,
+       ) -> Result<schnorr::Signature, ()> {
                todo!()
        }
 
-       fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<All>) -> Result<schnorr::Signature, ()> {
+       fn sign_justice_revoked_htlc(
+               &self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey,
+               htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<All>,
+       ) -> Result<schnorr::Signature, ()> {
                todo!()
        }
 
-       fn sign_holder_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1<All>) -> Result<schnorr::Signature, ()> {
+       fn sign_holder_htlc_transaction(
+               &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor,
+               secp_ctx: &Secp256k1<All>,
+       ) -> Result<schnorr::Signature, ()> {
                todo!()
        }
 
-       fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<All>) -> Result<schnorr::Signature, ()> {
+       fn sign_counterparty_htlc_transaction(
+               &self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey,
+               htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<All>,
+       ) -> Result<schnorr::Signature, ()> {
                todo!()
        }
 
-       fn partially_sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<All>) -> Result<PartialSignature, ()> {
+       fn partially_sign_closing_transaction(
+               &self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<All>,
+       ) -> Result<PartialSignature, ()> {
                todo!()
        }
 
-       fn sign_holder_anchor_input(&self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1<All>) -> Result<schnorr::Signature, ()> {
+       fn sign_holder_anchor_input(
+               &self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1<All>,
+       ) -> Result<schnorr::Signature, ()> {
                todo!()
        }
 }
@@ -1289,8 +1755,6 @@ 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);
@@ -1311,7 +1775,10 @@ impl Writeable for InMemorySigner {
        }
 }
 
-impl<ES: Deref> ReadableArgs<ES> for InMemorySigner where ES::Target: EntropySource {
+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);
 
@@ -1324,9 +1791,14 @@ impl<ES: Deref> ReadableArgs<ES> for InMemorySigner where ES::Target: EntropySou
                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 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, {});
@@ -1367,7 +1839,7 @@ pub struct KeysManager {
        inbound_payment_key: KeyMaterial,
        destination_script: ScriptBuf,
        shutdown_pubkey: PublicKey,
-       channel_master_key: ExtendedPrivKey,
+       channel_master_key: Xpriv,
        channel_child_index: AtomicUsize,
 
        entropy_source: RandomBytes,
@@ -1398,25 +1870,40 @@ impl KeysManager {
        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) {
+               match Xpriv::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_secret = master_key
+                                       .derive_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()) {
+                               let destination_script = match master_key
+                                       .derive_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)
+                                               let wpubkey_hash = WPubkeyHash::hash(
+                                                       &Xpub::from_priv(&secp_ctx, &destination_key).to_pub().to_bytes(),
+                                               );
+                                               Builder::new()
+                                                       .push_opcode(opcodes::all::OP_PUSHBYTES_0)
                                                        .push_slice(&wpubkey_hash.to_byte_array())
                                                        .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,
+                               let shutdown_pubkey = match master_key
+                                       .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(2).unwrap())
+                               {
+                                       Ok(shutdown_key) => Xpub::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 channel_master_key = master_key
+                                       .derive_priv(&secp_ctx, &ChildNumber::from_hardened_idx(3).unwrap())
+                                       .expect("Your RNG is busted");
+                               let inbound_payment_key: SecretKey = master_key
+                                       .derive_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[..]);
 
@@ -1425,7 +1912,8 @@ impl KeysManager {
                                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).to_byte_array();
+                               let rand_bytes_unique_start =
+                                       Sha256::from_engine(rand_bytes_engine).to_byte_array();
 
                                let mut res = KeysManager {
                                        secp_ctx,
@@ -1458,8 +1946,10 @@ impl KeysManager {
                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 {
+       /// Derive an old [`EcdsaChannelSigner`] 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);
@@ -1468,9 +1958,14 @@ impl KeysManager {
                // 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");
+               let child_privkey = self
+                       .channel_master_key
+                       .derive_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).to_byte_array();
@@ -1487,8 +1982,9 @@ impl KeysManager {
                                sha.input(&seed);
                                sha.input(&$prev_key[..]);
                                sha.input(&$info[..]);
-                               SecretKey::from_slice(&Sha256::from_engine(sha).to_byte_array()).expect("SHA-256 is busted")
-                       }}
+                               SecretKey::from_slice(&Sha256::from_engine(sha).to_byte_array())
+                                       .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);
@@ -1511,7 +2007,7 @@ impl KeysManager {
                )
        }
 
-       /// Signs the given [`PartiallySignedTransaction`] which spends the given [`SpendableOutputDescriptor`]s.
+       /// Signs the given [`Psbt`] 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.
        ///
@@ -1519,65 +2015,113 @@ impl KeysManager {
        ///
        /// 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], mut psbt: PartiallySignedTransaction, secp_ctx: &Secp256k1<C>) -> Result<PartiallySignedTransaction, ()> {
+       pub fn sign_spendable_outputs_psbt<C: Signing>(
+               &self, descriptors: &[&SpendableOutputDescriptor], mut psbt: Psbt, secp_ctx: &Secp256k1<C>,
+       ) -> Result<Psbt, ()> {
                let mut keys_cache: Option<(InMemorySigner, [u8; 32])> = None;
                for outp in descriptors {
+                       let get_input_idx = |outpoint: &OutPoint| {
+                               psbt.unsigned_tx
+                                       .input
+                                       .iter()
+                                       .position(|i| i.previous_output == outpoint.into_bitcoin_outpoint())
+                                       .ok_or(())
+                       };
                        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 {
-                                               let mut signer = self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id);
-                                               if let Some(channel_params) = descriptor.channel_transaction_parameters.as_ref() {
+                                       let input_idx = get_input_idx(&descriptor.outpoint)?;
+                                       if keys_cache.is_none()
+                                               || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id
+                                       {
+                                               let mut signer = self.derive_channel_keys(
+                                                       descriptor.channel_value_satoshis,
+                                                       &descriptor.channel_keys_id,
+                                               );
+                                               if let Some(channel_params) =
+                                                       descriptor.channel_transaction_parameters.as_ref()
+                                               {
                                                        signer.provide_channel_parameters(channel_params);
                                                }
                                                keys_cache = Some((signer, descriptor.channel_keys_id));
                                        }
-                                       let witness = keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&psbt.unsigned_tx, input_idx, &descriptor, &secp_ctx)?;
+                                       let witness = 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 {
+                                       let input_idx = get_input_idx(&descriptor.outpoint)?;
+                                       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));
+                                                       self.derive_channel_keys(
+                                                               descriptor.channel_value_satoshis,
+                                                               &descriptor.channel_keys_id,
+                                                       ),
+                                                       descriptor.channel_keys_id,
+                                               ));
                                        }
-                                       let witness = keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&psbt.unsigned_tx, input_idx, &descriptor, &secp_ctx)?;
+                                       let witness = 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 input_idx = get_input_idx(outpoint)?;
+                                       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) {
+                                               match Xpriv::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")) {
+                                                               match master_key.derive_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();
+                                       let pubkey = Xpub::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 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 sighash = hash_to_message!(
+                                               &sighash::SighashCache::new(&psbt.unsigned_tx)
+                                                       .p2wsh_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_slice(&[&sig_ser, &pubkey.inner.serialize().to_vec()]);
+                                       let witness =
+                                               Witness::from_slice(&[&sig_ser, &pubkey.inner.serialize().to_vec()]);
                                        psbt.inputs[input_idx].final_script_witness = Some(witness);
                                },
                        }
@@ -1585,37 +2129,6 @@ impl KeysManager {
 
                Ok(psbt)
        }
-
-       /// 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: ScriptBuf, feerate_sat_per_1000_weight: u32, locktime: Option<LockTime>, 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)?;
-               psbt = self.sign_spendable_outputs_psbt(descriptors, psbt, secp_ctx)?;
-
-               let spend_tx = psbt.extract_tx();
-
-               debug_assert!(expected_max_weight >= spend_tx.weight().to_wu());
-               // 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().to_wu() + descriptors.len() as u64 * 3);
-
-               Ok(spend_tx)
-       }
 }
 
 impl EntropySource for KeysManager {
@@ -1628,14 +2141,16 @@ 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(())
+                       Recipient::PhantomNode => Err(()),
                }
        }
 
-       fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()> {
+       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(())
+                       Recipient::PhantomNode => Err(()),
                }?;
                if let Some(tweak) = tweak {
                        node_secret = node_secret.mul_tweak(tweak).map_err(|_| ())?;
@@ -1647,29 +2162,34 @@ impl NodeSigner for KeysManager {
                self.inbound_payment_key.clone()
        }
 
-       fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
+       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(())
+                       Recipient::PhantomNode => Err(()),
                }?;
-               Ok(self.secp_ctx.sign_ecdsa_recoverable(&hash_to_message!(&Sha256::hash(&preimage).to_byte_array()), secret))
+               Ok(self.secp_ctx.sign_ecdsa_recoverable(
+                       &hash_to_message!(&Sha256::hash(&preimage).to_byte_array()),
+                       secret,
+               ))
        }
 
        fn sign_bolt12_invoice_request(
-               &self, invoice_request: &UnsignedInvoiceRequest
+               &self, invoice_request: &UnsignedInvoiceRequest,
        ) -> Result<schnorr::Signature, ()> {
                let message = invoice_request.tagged_hash().as_digest();
-               let keys = KeyPair::from_secret_key(&self.secp_ctx, &self.node_secret);
+               let keys = Keypair::from_secret_key(&self.secp_ctx, &self.node_secret);
                let aux_rand = self.get_secure_random_bytes();
                Ok(self.secp_ctx.sign_schnorr_with_aux_rand(message, &keys, &aux_rand))
        }
 
        fn sign_bolt12_invoice(
-               &self, invoice: &UnsignedBolt12Invoice
+               &self, invoice: &UnsignedBolt12Invoice,
        ) -> Result<schnorr::Signature, ()> {
                let message = invoice.tagged_hash().as_digest();
-               let keys = KeyPair::from_secret_key(&self.secp_ctx, &self.node_secret);
+               let keys = Keypair::from_secret_key(&self.secp_ctx, &self.node_secret);
                let aux_rand = self.get_secure_random_bytes();
                Ok(self.secp_ctx.sign_schnorr_with_aux_rand(message, &keys, &aux_rand))
        }
@@ -1680,12 +2200,53 @@ impl NodeSigner for KeysManager {
        }
 }
 
+impl OutputSpender for KeysManager {
+       /// 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).
+       ///
+       /// See [`OutputSpender::spend_spendable_outputs`] documentation for more information.
+       ///
+       /// 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`].
+       fn spend_spendable_outputs<C: Signing>(
+               &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
+               change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
+               locktime: Option<LockTime>, secp_ctx: &Secp256k1<C>,
+       ) -> Result<Transaction, ()> {
+               let (mut psbt, expected_max_weight) =
+                       SpendableOutputDescriptor::create_spendable_outputs_psbt(
+                               secp_ctx,
+                               descriptors,
+                               outputs,
+                               change_destination_script,
+                               feerate_sat_per_1000_weight,
+                               locktime,
+                       )?;
+               psbt = self.sign_spendable_outputs_psbt(descriptors, psbt, secp_ctx)?;
+
+               let spend_tx = psbt.extract_tx_unchecked_fee_rate();
+
+               debug_assert!(expected_max_weight >= spend_tx.weight().to_wu());
+               // 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().to_wu() + descriptors.len() as u64 * 3
+               );
+
+               Ok(spend_tx)
+       }
+}
+
 impl SignerProvider for KeysManager {
        type EcdsaSigner = InMemorySigner;
        #[cfg(taproot)]
        type TaprootSigner = InMemorySigner;
 
-       fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] {
+       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
@@ -1701,7 +2262,9 @@ impl SignerProvider for KeysManager {
                id
        }
 
-       fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::EcdsaSigner {
+       fn derive_channel_signer(
+               &self, channel_value_satoshis: u64, channel_keys_id: [u8; 32],
+       ) -> Self::EcdsaSigner {
                self.derive_channel_keys(channel_value_satoshis, &channel_keys_id)
        }
 
@@ -1760,7 +2323,9 @@ impl NodeSigner for PhantomKeysManager {
                }
        }
 
-       fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result<SharedSecret, ()> {
+       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(),
@@ -1775,23 +2340,28 @@ impl NodeSigner for PhantomKeysManager {
                self.inbound_payment_key.clone()
        }
 
-       fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
+       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).to_byte_array()), secret))
+               Ok(self.inner.secp_ctx.sign_ecdsa_recoverable(
+                       &hash_to_message!(&Sha256::hash(&preimage).to_byte_array()),
+                       secret,
+               ))
        }
 
        fn sign_bolt12_invoice_request(
-               &self, invoice_request: &UnsignedInvoiceRequest
+               &self, invoice_request: &UnsignedInvoiceRequest,
        ) -> Result<schnorr::Signature, ()> {
                self.inner.sign_bolt12_invoice_request(invoice_request)
        }
 
        fn sign_bolt12_invoice(
-               &self, invoice: &UnsignedBolt12Invoice
+               &self, invoice: &UnsignedBolt12Invoice,
        ) -> Result<schnorr::Signature, ()> {
                self.inner.sign_bolt12_invoice(invoice)
        }
@@ -1801,16 +2371,39 @@ impl NodeSigner for PhantomKeysManager {
        }
 }
 
+impl OutputSpender for PhantomKeysManager {
+       /// See [`OutputSpender::spend_spendable_outputs`] and [`KeysManager::spend_spendable_outputs`]
+       /// for documentation on this method.
+       fn spend_spendable_outputs<C: Signing>(
+               &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
+               change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
+               locktime: Option<LockTime>, secp_ctx: &Secp256k1<C>,
+       ) -> Result<Transaction, ()> {
+               self.inner.spend_spendable_outputs(
+                       descriptors,
+                       outputs,
+                       change_destination_script,
+                       feerate_sat_per_1000_weight,
+                       locktime,
+                       secp_ctx,
+               )
+       }
+}
+
 impl SignerProvider for PhantomKeysManager {
        type EcdsaSigner = InMemorySigner;
        #[cfg(taproot)]
        type TaprootSigner = InMemorySigner;
 
-       fn generate_channel_keys_id(&self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] {
+       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::EcdsaSigner {
+       fn derive_channel_signer(
+               &self, channel_value_satoshis: u64, channel_keys_id: [u8; 32],
+       ) -> Self::EcdsaSigner {
                self.inner.derive_channel_signer(channel_value_satoshis, channel_keys_id)
        }
 
@@ -1839,9 +2432,15 @@ impl PhantomKeysManager {
        /// 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 {
+       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 (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 {
@@ -1852,13 +2451,10 @@ impl PhantomKeysManager {
                }
        }
 
-       /// 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: ScriptBuf, feerate_sat_per_1000_weight: u32, locktime: Option<LockTime>, 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 {
+       pub fn derive_channel_keys(
+               &self, channel_value_satoshis: u64, params: &[u8; 32],
+       ) -> InMemorySigner {
                self.inner.derive_channel_keys(channel_value_satoshis, params)
        }
 
@@ -1887,10 +2483,7 @@ pub struct RandomBytes {
 impl RandomBytes {
        /// Creates a new instance using the given seed.
        pub fn new(seed: [u8; 32]) -> Self {
-               Self {
-                       seed,
-                       index: AtomicCounter::new(),
-               }
+               Self { seed, index: AtomicCounter::new() }
        }
 }
 
@@ -1911,13 +2504,13 @@ pub fn dyn_sign() {
 
 #[cfg(ldk_bench)]
 pub mod benches {
-       use std::sync::{Arc, mpsc};
+       use crate::sign::{EntropySource, KeysManager};
+       use bitcoin::blockdata::constants::genesis_block;
+       use bitcoin::Network;
        use std::sync::mpsc::TryRecvError;
+       use std::sync::{mpsc, Arc};
        use std::thread;
        use std::time::Duration;
-       use bitcoin::blockdata::constants::genesis_block;
-       use bitcoin::Network;
-       use crate::sign::{EntropySource, KeysManager};
 
        use criterion::Criterion;
 
@@ -1931,24 +2524,23 @@ pub mod benches {
                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) => {}
-                                       }
+                       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.bench_function("get_secure_random_bytes", |b| b.iter(||
-                       keys_manager.get_secure_random_bytes()));
+               bench.bench_function("get_secure_random_bytes", |b| {
+                       b.iter(|| keys_manager.get_secure_random_bytes())
+               });
 
                for stop in stops {
                        let _ = stop.send(());
index 230383f4f7d6496bcb1b7d40829b086ea6d39d05..7536b68a8793b59dc79a0a7661fdef8881712c4e 100644 (file)
@@ -3,11 +3,13 @@
 use alloc::vec::Vec;
 use bitcoin::blockdata::transaction::Transaction;
 use bitcoin::secp256k1;
-use bitcoin::secp256k1::{PublicKey, schnorr::Signature, Secp256k1, SecretKey};
+use bitcoin::secp256k1::{schnorr::Signature, PublicKey, Secp256k1, SecretKey};
 
 use musig2::types::{PartialSignature, PublicNonce};
 
-use crate::ln::chan_utils::{ClosingTransaction, CommitmentTransaction, HolderCommitmentTransaction, HTLCOutputInCommitment};
+use crate::ln::chan_utils::{
+       ClosingTransaction, CommitmentTransaction, HTLCOutputInCommitment, HolderCommitmentTransaction,
+};
 use crate::ln::msgs::PartialSignatureWithNonce;
 use crate::ln::PaymentPreimage;
 use crate::sign::{ChannelSigner, HTLCDescriptor};
@@ -18,7 +20,9 @@ use crate::sign::{ChannelSigner, HTLCDescriptor};
 pub trait TaprootChannelSigner: ChannelSigner {
        /// Generate a local nonce pair, which requires committing to ahead of time.
        /// The counterparty needs the public nonce generated herein to compute a partial signature.
-       fn generate_local_nonce_pair(&self, commitment_number: u64, secp_ctx: &Secp256k1<secp256k1::All>) -> PublicNonce;
+       fn generate_local_nonce_pair(
+               &self, commitment_number: u64, secp_ctx: &Secp256k1<secp256k1::All>,
+       ) -> PublicNonce;
 
        /// Create a signature for a counterparty's commitment transaction and associated HTLC transactions.
        ///
@@ -36,8 +40,8 @@ pub trait TaprootChannelSigner: ChannelSigner {
        /// irrelevant or duplicate preimages.
        //
        // TODO: Document the things someone using this interface should enforce before signing.
-       fn partially_sign_counterparty_commitment(&self, counterparty_nonce: PublicNonce,
-               commitment_tx: &CommitmentTransaction,
+       fn partially_sign_counterparty_commitment(
+               &self, counterparty_nonce: PublicNonce, commitment_tx: &CommitmentTransaction,
                inbound_htlc_preimages: Vec<PaymentPreimage>,
                outbound_htlc_preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>,
        ) -> Result<(PartialSignatureWithNonce, Vec<Signature>), ()>;
@@ -53,9 +57,10 @@ pub trait TaprootChannelSigner: ChannelSigner {
        /// An external signer implementation should check that the commitment has not been revoked.
        ///
        // TODO: Document the things someone using this interface should enforce before signing.
-       fn finalize_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction,
+       fn finalize_holder_commitment(
+               &self, commitment_tx: &HolderCommitmentTransaction,
                counterparty_partial_signature: PartialSignatureWithNonce,
-               secp_ctx: &Secp256k1<secp256k1::All>
+               secp_ctx: &Secp256k1<secp256k1::All>,
        ) -> Result<PartialSignature, ()>;
 
        /// Create a signature for the given input in a transaction spending an HTLC transaction output
@@ -72,8 +77,9 @@ pub trait TaprootChannelSigner: ChannelSigner {
        /// 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>,
+       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
@@ -94,9 +100,10 @@ pub trait TaprootChannelSigner: ChannelSigner {
        ///
        /// `htlc` holds HTLC elements (hash, timelock), thus changing the format of the witness script
        /// (which is committed to in the BIP 341 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, ()>;
+       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, ()>;
 
        /// 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
@@ -109,8 +116,9 @@ pub trait TaprootChannelSigner: ChannelSigner {
        ///
        /// [`TapSighashType::Default`]: bitcoin::sighash::TapSighashType::Default
        /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
-       fn sign_holder_htlc_transaction(&self, htlc_tx: &Transaction, input: usize,
-               htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1<secp256k1::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
@@ -130,16 +138,18 @@ pub trait TaprootChannelSigner: ChannelSigner {
        /// 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 341 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, ()>;
+       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 partially_sign_closing_transaction(&self, closing_tx: &ClosingTransaction,
-               secp_ctx: &Secp256k1<secp256k1::All>) -> Result<PartialSignature, ()>;
+       fn partially_sign_closing_transaction(
+               &self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>,
+       ) -> Result<PartialSignature, ()>;
 
        /// 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`.
index 2a122da34470332e1147a427de86c864abe4adab..fad8c0ac96c0689fecfb7858f346aae63afe983c 100644 (file)
@@ -1,14 +1,20 @@
-use core::ops::Deref;
 use crate::sign::{ChannelSigner, SignerProvider};
+use core::ops::Deref;
 
-pub(crate) enum ChannelSignerType<SP: Deref> where SP::Target: SignerProvider {
+pub(crate) enum ChannelSignerType<SP: Deref>
+where
+       SP::Target: SignerProvider,
+{
        // in practice, this will only ever be an EcdsaChannelSigner (specifically, Writeable)
        Ecdsa(<SP::Target as SignerProvider>::EcdsaSigner),
        #[cfg(taproot)]
        Taproot(<SP::Target as SignerProvider>::TaprootSigner),
 }
 
-impl<SP: Deref> ChannelSignerType<SP> where SP::Target: SignerProvider {
+impl<SP: Deref> ChannelSignerType<SP>
+where
+       SP::Target: SignerProvider,
+{
        pub(crate) fn as_ref(&self) -> &dyn ChannelSigner {
                match self {
                        ChannelSignerType::Ecdsa(ecs) => ecs,
@@ -29,15 +35,17 @@ impl<SP: Deref> ChannelSignerType<SP> where SP::Target: SignerProvider {
        pub(crate) fn as_ecdsa(&self) -> Option<&<SP::Target as SignerProvider>::EcdsaSigner> {
                match self {
                        ChannelSignerType::Ecdsa(ecs) => Some(ecs),
-                       _ => None
+                       _ => None,
                }
        }
 
        #[allow(unused)]
-       pub(crate) fn as_mut_ecdsa(&mut self) -> Option<&mut <SP::Target as SignerProvider>::EcdsaSigner> {
+       pub(crate) fn as_mut_ecdsa(
+               &mut self,
+       ) -> Option<&mut <SP::Target as SignerProvider>::EcdsaSigner> {
                match self {
                        ChannelSignerType::Ecdsa(ecs) => Some(ecs),
-                       _ => None
+                       _ => None,
                }
        }
 }
index 2b75e095380ec09f22d07d4d1bd636dd93e53c61..5968a79ee4daa50439a3cac96478c49d3803458b 100644 (file)
@@ -103,7 +103,9 @@ fn locate_call_symbol(backtrace: &Backtrace) -> (String, Option<u32>) {
                        }
                }
        }
-       let symbol = symbol_after_latest_debug_sync.expect("Couldn't find lock call symbol");
+       let symbol = symbol_after_latest_debug_sync.unwrap_or_else(|| {
+               panic!("Couldn't find lock call symbol in trace {:?}", backtrace);
+       });
        (format!("{}:{}", symbol.filename().unwrap().display(), symbol.lineno().unwrap()), symbol.colno())
 }
 
index 2e66d59383fb9d7db7d81fdbafdf1beb8d4e31d7..e116dccbf59ad5bee31f1aa50bb7e2323f127774 100644 (file)
@@ -6,7 +6,7 @@
 // Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or
 // MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) at your option.
 
-
+#[allow(unused)]
 use crate::prelude::*;
 
 /// RFC4648 encoding table
index 2c8f03b93c89528798249868d98178cf0e23c052..2473eea26274500800a4e5ea11c9ce9e6530fde4 100644 (file)
@@ -360,7 +360,7 @@ impl Readable for ChannelHandshakeLimits {
        }
 }
 
-/// Options for how to set the max dust HTLC exposure allowed on a channel. See
+/// Options for how to set the max dust exposure allowed on a channel. See
 /// [`ChannelConfig::max_dust_htlc_exposure`] for details.
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum MaxDustHTLCExposure {
@@ -374,19 +374,17 @@ pub enum MaxDustHTLCExposure {
        /// to this maximum the channel may be unable to send/receive HTLCs between the maximum dust
        /// exposure and the new minimum value for HTLCs to be economically viable to claim.
        FixedLimitMsat(u64),
-       /// This sets a multiplier on the estimated high priority feerate (sats/KW, as obtained from
-       /// [`FeeEstimator`]) to determine the maximum allowed dust exposure. If this variant is used
-       /// then the maximum dust exposure in millisatoshis is calculated as:
-       /// `high_priority_feerate_per_kw * value`. For example, with our default value
-       /// `FeeRateMultiplier(5000)`:
+       /// This sets a multiplier on the [`ConfirmationTarget::OnChainSweep`] feerate (in sats/KW) to
+       /// determine the maximum allowed dust exposure. If this variant is used then the maximum dust
+       /// exposure in millisatoshis is calculated as:
+       /// `feerate_per_kw * value`. For example, with our default value
+       /// `FeeRateMultiplier(10_000)`:
        ///
        /// - For the minimum fee rate of 1 sat/vByte (250 sat/KW, although the minimum
        /// defaults to 253 sats/KW for rounding, see [`FeeEstimator`]), the max dust exposure would
-       /// be 253 * 5000 = 1,265,000 msats.
+       /// be 253 * 10_000 = 2,530,000 msats.
        /// - For a fee rate of 30 sat/vByte (7500 sat/KW), the max dust exposure would be
-       /// 7500 * 5000 = 37,500,000 msats.
-       ///
-       /// This allows the maximum dust exposure to automatically scale with fee rate changes.
+       /// 7500 * 50_000 = 75,000,000 msats (0.00075 BTC).
        ///
        /// Note, if you're using a third-party fee estimator, this may leave you more exposed to a
        /// fee griefing attack, where your fee estimator may purposely overestimate the fee rate,
@@ -401,6 +399,7 @@ pub enum MaxDustHTLCExposure {
        /// by default this will be set to a [`Self::FixedLimitMsat`] of 5,000,000 msat.
        ///
        /// [`FeeEstimator`]: crate::chain::chaininterface::FeeEstimator
+       /// [`ConfirmationTarget::OnChainSweep`]: crate::chain::chaininterface::ConfirmationTarget::OnChainSweep
        FeeRateMultiplier(u64),
 }
 
@@ -453,13 +452,16 @@ pub struct ChannelConfig {
        ///
        /// [`MIN_CLTV_EXPIRY_DELTA`]: crate::ln::channelmanager::MIN_CLTV_EXPIRY_DELTA
        pub cltv_expiry_delta: u16,
-       /// Limit our total exposure to in-flight HTLCs which are burned to fees as they are too
-       /// small to claim on-chain.
+       /// Limit our total exposure to potential loss to on-chain fees on close, including in-flight
+       /// HTLCs which are burned to fees as they are too small to claim on-chain and fees on
+       /// commitment transaction(s) broadcasted by our counterparty in excess of our own fee estimate.
+       ///
+       /// # HTLC-based Dust Exposure
        ///
        /// When an HTLC present in one of our channels is below a "dust" threshold, the HTLC will
        /// not be claimable on-chain, instead being turned into additional miner fees if either
        /// party force-closes the channel. Because the threshold is per-HTLC, our total exposure
-       /// to such payments may be sustantial if there are many dust HTLCs present when the
+       /// to such payments may be substantial if there are many dust HTLCs present when the
        /// channel is force-closed.
        ///
        /// The dust threshold for each HTLC is based on the `dust_limit_satoshis` for each party in a
@@ -473,7 +475,42 @@ pub struct ChannelConfig {
        /// The selected limit is applied for sent, forwarded, and received HTLCs and limits the total
        /// exposure across all three types per-channel.
        ///
-       /// Default value: [`MaxDustHTLCExposure::FeeRateMultiplier`] with a multiplier of 5000.
+       /// # Transaction Fee Dust Exposure
+       ///
+       /// Further, counterparties broadcasting a commitment transaction in a force-close may result
+       /// in other balance being burned to fees, and thus all fees on commitment and HTLC
+       /// transactions in excess of our local fee estimates are included in the dust calculation.
+       ///
+       /// Because of this, another way to look at this limit is to divide it by 43,000 (or 218,750
+       /// for non-anchor channels) and see it as the maximum feerate disagreement (in sats/vB) per
+       /// non-dust HTLC we're allowed to have with our peers before risking a force-closure for
+       /// inbound channels.
+       // This works because, for anchor channels the on-chain cost is 172 weight (172+703 for
+       // non-anchors with an HTLC-Success transaction), i.e.
+       // dust_exposure_limit_msat / 1000 = 172 * feerate_in_sat_per_vb / 4 * HTLC count
+       // dust_exposure_limit_msat = 43,000 * feerate_in_sat_per_vb * HTLC count
+       // dust_exposure_limit_msat / HTLC count / 43,000 = feerate_in_sat_per_vb
+       ///
+       /// Thus, for the default value of 10_000 * a current feerate estimate of 10 sat/vB (or 2,500
+       /// sat/KW), we risk force-closure if we disagree with our peer by:
+       /// * `10_000 * 2_500 / 43_000 / (483*2)` = 0.6 sat/vB for anchor channels with 483 HTLCs in
+       ///   both directions (the maximum),
+       /// * `10_000 * 2_500 / 43_000 / (50*2)` = 5.8 sat/vB for anchor channels with 50 HTLCs in both
+       ///   directions (the LDK default max from [`ChannelHandshakeConfig::our_max_accepted_htlcs`])
+       /// * `10_000 * 2_500 / 218_750 / (483*2)` = 0.1 sat/vB for non-anchor channels with 483 HTLCs
+       ///   in both directions (the maximum),
+       /// * `10_000 * 2_500 / 218_750 / (50*2)` = 1.1 sat/vB for non-anchor channels with 50 HTLCs
+       ///   in both (the LDK default maximum from [`ChannelHandshakeConfig::our_max_accepted_htlcs`])
+       ///
+       /// Note that when using [`MaxDustHTLCExposure::FeeRateMultiplier`] this maximum disagreement
+       /// will scale linearly with increases (or decreases) in the our feerate estimates. Further,
+       /// for anchor channels we expect our counterparty to use a relatively low feerate estimate
+       /// while we use [`ConfirmationTarget::OnChainSweep`] (which should be relatively high) and
+       /// feerate disagreement force-closures should only occur when theirs is higher than ours.
+       ///
+       /// Default value: [`MaxDustHTLCExposure::FeeRateMultiplier`] with a multiplier of 10_000.
+       ///
+       /// [`ConfirmationTarget::OnChainSweep`]: crate::chain::chaininterface::ConfirmationTarget::OnChainSweep
        pub max_dust_htlc_exposure: MaxDustHTLCExposure,
        /// The additional fee we're willing to pay to avoid waiting for the counterparty's
        /// `to_self_delay` to reclaim funds.
@@ -561,7 +598,7 @@ impl Default for ChannelConfig {
                        forwarding_fee_proportional_millionths: 0,
                        forwarding_fee_base_msat: 1000,
                        cltv_expiry_delta: 6 * 12, // 6 blocks/hour * 12 hours
-                       max_dust_htlc_exposure: MaxDustHTLCExposure::FeeRateMultiplier(5000),
+                       max_dust_htlc_exposure: MaxDustHTLCExposure::FeeRateMultiplier(10000),
                        force_close_avoidance_max_fee_satoshis: 1000,
                        accept_underpaying_htlcs: false,
                }
index 4ffde9a72d2cba94927e36373125d0d06fdb73d4..735ce044f810b6a4f9aa97e500c1c628cd43ee4d 100644 (file)
@@ -11,7 +11,9 @@
 
 use crate::ln::script::ShutdownScript;
 
-use alloc::string::String;
+#[allow(unused_imports)]
+use crate::prelude::*;
+
 use core::fmt;
 
 /// Indicates an error on the client's part (usually some variant of attempting to use too-low or
index afe9c402c713981a5860002655aa101442fad37e..dec3d8f6590ac44eef0e1b2754e9c88191c4f0e0 100644 (file)
@@ -12,13 +12,13 @@ macro_rules! hash_to_message {
                {
                        #[cfg(not(fuzzing))]
                        {
-                               ::bitcoin::secp256k1::Message::from_slice($slice).unwrap()
+                               ::bitcoin::secp256k1::Message::from_digest_slice($slice).unwrap()
                        }
                        #[cfg(fuzzing)]
                        {
-                               match ::bitcoin::secp256k1::Message::from_slice($slice) {
+                               match ::bitcoin::secp256k1::Message::from_digest_slice($slice) {
                                        Ok(msg) => msg,
-                                       Err(_) => ::bitcoin::secp256k1::Message::from_slice(&[1; 32]).unwrap()
+                                       Err(_) => ::bitcoin::secp256k1::Message::from_digest([1; 32])
                                }
                        }
                }
index d4c20f722154fe499c68a296c45f25d8dd55aff4..97788ffe68acbd5f90d67bd501710c3b5942ce51 100644 (file)
@@ -1,10 +1,8 @@
 //! This module has a map which can be iterated in a deterministic order. See the [`IndexedMap`].
 
 use crate::prelude::*;
-use alloc::vec::Vec;
 use alloc::slice::Iter;
 use core::hash::Hash;
-use core::cmp::Ord;
 use core::ops::{Bound, RangeBounds};
 
 /// A map which can be iterated in a deterministic order.
@@ -58,6 +56,11 @@ impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
                self.map.get_mut(key)
        }
 
+       /// Fetches the key-value pair corresponding to the supplied key, if one exists.
+       pub fn get_key_value(&self, key: &K) -> Option<(&K, &V)> {
+               self.map.get_key_value(key)
+       }
+
        #[inline]
        /// Returns true if an element with the given `key` exists in the map.
        pub fn contains_key(&self, key: &K) -> bool {
index 8c22200b9b2c2fecebfb4276228a91ce97db1274..4a4baa45267673b3abddac845918c52a64a2dcde 100644 (file)
@@ -1,6 +1,8 @@
 //! Low level invoice utilities.
 
-use bitcoin::bech32::{u5, FromBase32};
+use bech32::{u5, FromBase32};
+
+#[allow(unused)]
 use crate::prelude::*;
 
 /// Construct the invoice's HRP and signatureless data into a preimage to be hashed.
index e48cefaa0443ff9c0a1f731a35fdc73fba57d17b..33ef9b1f7838694bcc749903a1a39ad7d3fae8f2 100644 (file)
@@ -20,7 +20,8 @@ use core::cmp;
 use core::fmt;
 use core::ops::Deref;
 
-use crate::ln::ChannelId;
+use crate::ln::types::ChannelId;
+use crate::ln::PaymentHash;
 #[cfg(c_bindings)]
 use crate::prelude::*; // Needed for String
 
@@ -120,6 +121,11 @@ pub struct Record<$($args)?> {
        pub file: &'static str,
        /// The line containing the message.
        pub line: u32,
+       /// The payment hash.
+       ///
+       /// Note that this is only filled in for logs pertaining to a specific payment, and will be
+       /// `None` for logs which are not directly related to a payment.
+       pub payment_hash: Option<PaymentHash>,
 }
 
 impl<$($args)?> Record<$($args)?> {
@@ -129,7 +135,8 @@ impl<$($args)?> Record<$($args)?> {
        #[inline]
        pub fn new<$($nonstruct_args)?>(
                level: Level, peer_id: Option<PublicKey>, channel_id: Option<ChannelId>,
-               args: fmt::Arguments<'a>, module_path: &'static str, file: &'static str, line: u32
+               args: fmt::Arguments<'a>, module_path: &'static str, file: &'static str, line: u32,
+               payment_hash: Option<PaymentHash>
        ) -> Record<$($args)?> {
                Record {
                        level,
@@ -142,6 +149,7 @@ impl<$($args)?> Record<$($args)?> {
                        module_path,
                        file,
                        line,
+                       payment_hash,
                }
        }
 }
@@ -168,6 +176,8 @@ pub struct WithContext<'a, L: Deref> where L::Target: Logger {
        peer_id: Option<PublicKey>,
        /// The channel id of the channel pertaining to the logged record.
        channel_id: Option<ChannelId>,
+       /// The payment hash of the payment pertaining to the logged record.
+       payment_hash: Option<PaymentHash>
 }
 
 impl<'a, L: Deref> Logger for WithContext<'a, L> where L::Target: Logger {
@@ -178,17 +188,21 @@ impl<'a, L: Deref> Logger for WithContext<'a, L> where L::Target: Logger {
                if self.channel_id.is_some() {
                        record.channel_id = self.channel_id;
                }
+               if self.payment_hash.is_some() {
+                       record.payment_hash = self.payment_hash;
+               }
                self.logger.log(record)
        }
 }
 
 impl<'a, L: Deref> WithContext<'a, L> where L::Target: Logger {
        /// Wraps the given logger, providing additional context to any logged records.
-       pub fn from(logger: &'a L, peer_id: Option<PublicKey>, channel_id: Option<ChannelId>) -> Self {
+       pub fn from(logger: &'a L, peer_id: Option<PublicKey>, channel_id: Option<ChannelId>, payment_hash: Option<PaymentHash>) -> Self {
                WithContext {
                        logger,
                        peer_id,
                        channel_id,
+                       payment_hash,
                }
        }
 }
@@ -244,7 +258,8 @@ impl<T: fmt::Display, I: core::iter::Iterator<Item = T> + Clone> fmt::Display fo
 #[cfg(test)]
 mod tests {
        use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1};
-       use crate::ln::ChannelId;
+       use crate::ln::types::ChannelId;
+       use crate::ln::PaymentHash;
        use crate::util::logger::{Logger, Level, WithContext};
        use crate::util::test_utils::TestLogger;
        use crate::sync::Arc;
@@ -291,7 +306,8 @@ mod tests {
                let logger = &TestLogger::new();
                let secp_ctx = Secp256k1::new();
                let pk = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
-               let context_logger = WithContext::from(&logger, Some(pk), Some(ChannelId([0; 32])));
+               let payment_hash = PaymentHash([0; 32]);
+               let context_logger = WithContext::from(&logger, Some(pk), Some(ChannelId([0; 32])), Some(payment_hash));
                log_error!(context_logger, "This is an error");
                log_warn!(context_logger, "This is an error");
                log_debug!(context_logger, "This is an error");
@@ -308,8 +324,9 @@ mod tests {
                let logger = &TestLogger::new();
                let secp_ctx = Secp256k1::new();
                let pk = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
-               let context_logger = &WithContext::from(&logger, None, Some(ChannelId([0; 32])));
-               let full_context_logger = WithContext::from(&context_logger, Some(pk), None);
+               let payment_hash = PaymentHash([0; 32]);
+               let context_logger = &WithContext::from(&logger, None, Some(ChannelId([0; 32])), Some(payment_hash));
+               let full_context_logger = WithContext::from(&context_logger, Some(pk), None, None);
                log_error!(full_context_logger, "This is an error");
                log_warn!(full_context_logger, "This is an error");
                log_debug!(full_context_logger, "This is an error");
index f962251cd65bd7baf072a9bcbfa0d731d4bfec64..123452806107c1a4c957ebe5a80c2994dea15fc5 100644 (file)
@@ -7,7 +7,7 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
-use crate::ln::ChannelId;
+use crate::ln::types::ChannelId;
 use crate::sign::SpendableOutputDescriptor;
 
 use bitcoin::blockdata::transaction::Transaction;
@@ -148,7 +148,7 @@ macro_rules! log_spendable {
 #[macro_export]
 macro_rules! log_internal {
        ($logger: expr, $lvl:expr, $($arg:tt)+) => (
-               $logger.log($crate::util::logger::Record::new($lvl, None, None, format_args!($($arg)+), module_path!(), file!(), line!()))
+               $logger.log($crate::util::logger::Record::new($lvl, None, None, format_args!($($arg)+), module_path!(), file!(), line!(), None))
        );
 }
 
index 88ff18ff204c8ae96ee723bd0a718bed0ae3394a..0a45af26f8633e27fa97ad783f2a7e32079bd94f 100644 (file)
@@ -20,6 +20,7 @@
 //! <https://lightning.readthedocs.io/lightning-signmessage.7.html>
 //! <https://api.lightning.community/#signmessage>
 
+#[allow(unused)]
 use crate::prelude::*;
 use crate::util::base32;
 use bitcoin::hashes::{sha256d, Hash};
@@ -53,12 +54,12 @@ fn sigrec_decode(sig_rec: Vec<u8>) -> Result<RecoverableSignature, Error> {
 /// Creates a digital signature of a message given a SecretKey, like the node's secret.
 /// A receiver knowing the PublicKey (e.g. the node's id) and the message can be sure that the signature was generated by the caller.
 /// Signatures are EC recoverable, meaning that given the message and the signature the PublicKey of the signer can be extracted.
-pub fn sign(msg: &[u8], sk: &SecretKey) -> Result<String, Error> {
+pub fn sign(msg: &[u8], sk: &SecretKey) -> String {
        let secp_ctx = Secp256k1::signing_only();
        let msg_hash = sha256d::Hash::hash(&[LN_MESSAGE_PREFIX, msg].concat());
 
-       let sig = secp_ctx.sign_ecdsa_recoverable(&Message::from_slice(msg_hash.as_byte_array())?, sk);
-       Ok(base32::Alphabet::ZBase32.encode(&sigrec_encode(sig)))
+       let sig = secp_ctx.sign_ecdsa_recoverable(&Message::from_digest(msg_hash.to_byte_array()), sk);
+       base32::Alphabet::ZBase32.encode(&sigrec_encode(sig))
 }
 
 /// Recovers the PublicKey of the signer of the message given the message and the signature.
@@ -69,7 +70,7 @@ pub fn recover_pk(msg: &[u8], sig: &str) ->  Result<PublicKey, Error> {
        match base32::Alphabet::ZBase32.decode(&sig) {
                Ok(sig_rec) => {
                        match sigrec_decode(sig_rec) {
-                               Ok(sig) => secp_ctx.recover_ecdsa(&Message::from_slice(msg_hash.as_byte_array())?, &sig),
+                               Ok(sig) => secp_ctx.recover_ecdsa(&Message::from_digest(msg_hash.to_byte_array()), &sig),
                                Err(e) => Err(e)
                        }
                },
@@ -99,7 +100,7 @@ mod test {
                let one_key = SecretKey::from_slice(&ONE).unwrap();
                let zbase32_sig = sign(message.as_bytes(), &one_key);
 
-               assert_eq!(zbase32_sig.unwrap(), "d9tibmnic9t5y41hg7hkakdcra94akas9ku3rmmj4ag9mritc8ok4p5qzefs78c9pqfhpuftqqzhydbdwfg7u6w6wdxcqpqn4sj4e73e")
+               assert_eq!(zbase32_sig, "d9tibmnic9t5y41hg7hkakdcra94akas9ku3rmmj4ag9mritc8ok4p5qzefs78c9pqfhpuftqqzhydbdwfg7u6w6wdxcqpqn4sj4e73e")
        }
 
        #[test]
@@ -116,7 +117,7 @@ mod test {
        fn test_verify() {
                let message = "another message";
                let one_key = SecretKey::from_slice(&ONE).unwrap();
-               let sig = sign(message.as_bytes(), &one_key).unwrap();
+               let sig = sign(message.as_bytes(), &one_key);
                let pk = PublicKey::from_secret_key(&Secp256k1::signing_only(), &one_key);
 
                assert!(verify(message.as_bytes(), &sig, &pk))
index 31bdf1ca53c2562836e49cb5721e5a0950f2c070..c1ab8c75c2ee70efb51e7bb214ee76a8d06bef03 100644 (file)
@@ -22,6 +22,7 @@ pub mod invoice;
 pub mod persist;
 pub mod scid_utils;
 pub mod string;
+pub mod sweep;
 pub mod wakers;
 #[cfg(fuzzing)]
 pub mod base32;
index 2f418a8efc284a4de2d60be4b2b958b369518529..d03474b292ade70f28993eee63c8521e42b2f443 100644 (file)
@@ -7,25 +7,24 @@
 //! This module contains a simple key-value store trait [`KVStore`] that
 //! allows one to implement the persistence for [`ChannelManager`], [`NetworkGraph`],
 //! and [`ChannelMonitor`] all in one place.
+//!
+//! [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
 
 use core::cmp;
-use core::convert::{TryFrom, TryInto};
 use core::ops::Deref;
 use core::str::FromStr;
 use bitcoin::{BlockHash, Txid};
 
 use crate::{io, log_error};
-use crate::alloc::string::ToString;
 use crate::prelude::*;
 
 use crate::chain;
 use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
-use crate::chain::chainmonitor::{Persist, MonitorUpdateId};
-use crate::sign::{EntropySource, NodeSigner, ecdsa::WriteableEcdsaChannelSigner, SignerProvider};
+use crate::chain::chainmonitor::Persist;
+use crate::sign::{EntropySource, ecdsa::EcdsaChannelSigner, SignerProvider};
 use crate::chain::transaction::OutPoint;
 use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, CLOSED_CHANNEL_UPDATE_ID};
-use crate::ln::channelmanager::ChannelManager;
-use crate::routing::router::Router;
+use crate::ln::channelmanager::AChannelManager;
 use crate::routing::gossip::NetworkGraph;
 use crate::routing::scoring::WriteableScore;
 use crate::util::logger::Logger;
@@ -38,10 +37,16 @@ pub const KVSTORE_NAMESPACE_KEY_ALPHABET: &str = "abcdefghijklmnopqrstuvwxyzABCD
 pub const KVSTORE_NAMESPACE_KEY_MAX_LEN: usize = 120;
 
 /// The primary namespace under which the [`ChannelManager`] will be persisted.
+///
+/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
 pub const CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE: &str = "";
 /// The secondary namespace under which the [`ChannelManager`] will be persisted.
+///
+/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
 pub const CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE: &str = "";
 /// The key under which the [`ChannelManager`] will be persisted.
+///
+/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
 pub const CHANNEL_MANAGER_PERSISTENCE_KEY: &str = "manager";
 
 /// The primary namespace under which [`ChannelMonitor`]s will be persisted.
@@ -51,6 +56,11 @@ pub const CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE: &str = "";
 /// The primary namespace under which [`ChannelMonitorUpdate`]s will be persisted.
 pub const CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE: &str = "monitor_updates";
 
+/// The primary namespace under which archived [`ChannelMonitor`]s will be persisted.
+pub const ARCHIVED_CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE: &str = "archived_monitors";
+/// The secondary namespace under which archived [`ChannelMonitor`]s will be persisted.
+pub const ARCHIVED_CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE: &str = "";
+
 /// The primary namespace under which the [`NetworkGraph`] will be persisted.
 pub const NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE: &str = "";
 /// The secondary namespace under which the [`NetworkGraph`] will be persisted.
@@ -65,6 +75,20 @@ pub const SCORER_PERSISTENCE_SECONDARY_NAMESPACE: &str = "";
 /// The key under which the [`WriteableScore`] will be persisted.
 pub const SCORER_PERSISTENCE_KEY: &str = "scorer";
 
+/// The primary namespace under which [`OutputSweeper`] state will be persisted.
+///
+/// [`OutputSweeper`]: crate::util::sweep::OutputSweeper
+pub const OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE: &str = "";
+/// The secondary namespace under which [`OutputSweeper`] state will be persisted.
+///
+/// [`OutputSweeper`]: crate::util::sweep::OutputSweeper
+pub const OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE: &str = "";
+/// The secondary namespace under which [`OutputSweeper`] state will be persisted.
+/// The key under which [`OutputSweeper`] state will be persisted.
+///
+/// [`OutputSweeper`]: crate::util::sweep::OutputSweeper
+pub const OUTPUT_SWEEPER_PERSISTENCE_KEY: &str = "output_sweeper";
+
 /// A sentinel value to be prepended to monitors persisted by the [`MonitorUpdatingPersister`].
 ///
 /// This serves to prevent someone from accidentally loading such monitors (which may need
@@ -131,18 +155,17 @@ pub trait KVStore {
 }
 
 /// Trait that handles persisting a [`ChannelManager`], [`NetworkGraph`], and [`WriteableScore`] to disk.
-pub trait Persister<'a, M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref, S: WriteableScore<'a>>
-       where M::Target: 'static + chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
-               T::Target: 'static + BroadcasterInterface,
-               ES::Target: 'static + EntropySource,
-               NS::Target: 'static + NodeSigner,
-               SP::Target: 'static + SignerProvider,
-               F::Target: 'static + FeeEstimator,
-               R::Target: 'static + Router,
-               L::Target: 'static + Logger,
+///
+/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+pub trait Persister<'a, CM: Deref, L: Deref, S: WriteableScore<'a>>
+where
+       CM::Target: 'static + AChannelManager,
+       L::Target: 'static + Logger,
 {
        /// Persist the given ['ChannelManager'] to disk, returning an error if persistence failed.
-       fn persist_manager(&self, channel_manager: &ChannelManager<M, T, ES, NS, SP, F, R, L>) -> Result<(), io::Error>;
+       ///
+       /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+       fn persist_manager(&self, channel_manager: &CM) -> Result<(), io::Error>;
 
        /// Persist the given [`NetworkGraph`] to disk, returning an error if persistence failed.
        fn persist_graph(&self, network_graph: &NetworkGraph<L>) -> Result<(), io::Error>;
@@ -152,60 +175,18 @@ pub trait Persister<'a, M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F:
 }
 
 
-impl<'a, A: KVStore, M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref, S: WriteableScore<'a>> Persister<'a, M, T, ES, NS, SP, F, R, L, S> for A
-       where M::Target: 'static + chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
-               T::Target: 'static + BroadcasterInterface,
-               ES::Target: 'static + EntropySource,
-               NS::Target: 'static + NodeSigner,
-               SP::Target: 'static + SignerProvider,
-               F::Target: 'static + FeeEstimator,
-               R::Target: 'static + Router,
-               L::Target: 'static + Logger,
-{
-       /// Persist the given [`ChannelManager`] to disk, returning an error if persistence failed.
-       fn persist_manager(&self, channel_manager: &ChannelManager<M, T, ES, NS, SP, F, R, L>) -> Result<(), io::Error> {
-               self.write(CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE,
-                       CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE,
-                       CHANNEL_MANAGER_PERSISTENCE_KEY,
-                       &channel_manager.encode())
-       }
-
-       /// Persist the given [`NetworkGraph`] to disk, returning an error if persistence failed.
-       fn persist_graph(&self, network_graph: &NetworkGraph<L>) -> Result<(), io::Error> {
-               self.write(NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE,
-                       NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE,
-                       NETWORK_GRAPH_PERSISTENCE_KEY,
-                       &network_graph.encode())
-       }
-
-       /// Persist the given [`WriteableScore`] to disk, returning an error if persistence failed.
-       fn persist_scorer(&self, scorer: &S) -> Result<(), io::Error> {
-               self.write(SCORER_PERSISTENCE_PRIMARY_NAMESPACE,
-                       SCORER_PERSISTENCE_SECONDARY_NAMESPACE,
-                       SCORER_PERSISTENCE_KEY,
-                       &scorer.encode())
-       }
-}
-
-impl<'a, M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref, S: WriteableScore<'a>> Persister<'a, M, T, ES, NS, SP, F, R, L, S> for dyn KVStore + Send + Sync
-       where M::Target: 'static + chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
-               T::Target: 'static + BroadcasterInterface,
-               ES::Target: 'static + EntropySource,
-               NS::Target: 'static + NodeSigner,
-               SP::Target: 'static + SignerProvider,
-               F::Target: 'static + FeeEstimator,
-               R::Target: 'static + Router,
-               L::Target: 'static + Logger,
+impl<'a, A: KVStore + ?Sized, CM: Deref, L: Deref, S: WriteableScore<'a>> Persister<'a, CM, L, S> for A
+where
+       CM::Target: 'static + AChannelManager,
+       L::Target: 'static + Logger,
 {
-       /// Persist the given [`ChannelManager`] to disk, returning an error if persistence failed.
-       fn persist_manager(&self, channel_manager: &ChannelManager<M, T, ES, NS, SP, F, R, L>) -> Result<(), io::Error> {
+       fn persist_manager(&self, channel_manager: &CM) -> Result<(), io::Error> {
                self.write(CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE,
                        CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE,
                        CHANNEL_MANAGER_PERSISTENCE_KEY,
-                       &channel_manager.encode())
+                       &channel_manager.get_cm().encode())
        }
 
-       /// Persist the given [`NetworkGraph`] to disk, returning an error if persistence failed.
        fn persist_graph(&self, network_graph: &NetworkGraph<L>) -> Result<(), io::Error> {
                self.write(NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE,
                        NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE,
@@ -213,7 +194,6 @@ impl<'a, M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref
                        &network_graph.encode())
        }
 
-       /// Persist the given [`WriteableScore`] to disk, returning an error if persistence failed.
        fn persist_scorer(&self, scorer: &S) -> Result<(), io::Error> {
                self.write(SCORER_PERSISTENCE_PRIMARY_NAMESPACE,
                        SCORER_PERSISTENCE_SECONDARY_NAMESPACE,
@@ -222,13 +202,13 @@ impl<'a, M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref
        }
 }
 
-impl<ChannelSigner: WriteableEcdsaChannelSigner, K: KVStore> Persist<ChannelSigner> for K {
+impl<ChannelSigner: EcdsaChannelSigner, K: KVStore + ?Sized> Persist<ChannelSigner> for K {
        // TODO: We really need a way for the persister to inform the user that its time to crash/shut
        // down once these start returning failure.
        // Then we should return InProgress rather than UnrecoverableError, implying we should probably
        // just shut down the node since we're not retrying persistence!
 
-       fn persist_new_channel(&self, funding_txo: OutPoint, monitor: &ChannelMonitor<ChannelSigner>, _update_id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus {
+       fn persist_new_channel(&self, funding_txo: OutPoint, monitor: &ChannelMonitor<ChannelSigner>) -> chain::ChannelMonitorUpdateStatus {
                let key = format!("{}_{}", funding_txo.txid.to_string(), funding_txo.index);
                match self.write(
                        CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE,
@@ -240,7 +220,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner, K: KVStore> Persist<ChannelSign
                }
        }
 
-       fn update_persisted_channel(&self, funding_txo: OutPoint, _update: Option<&ChannelMonitorUpdate>, monitor: &ChannelMonitor<ChannelSigner>, _update_id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus {
+       fn update_persisted_channel(&self, funding_txo: OutPoint, _update: Option<&ChannelMonitorUpdate>, monitor: &ChannelMonitor<ChannelSigner>) -> chain::ChannelMonitorUpdateStatus {
                let key = format!("{}_{}", funding_txo.txid.to_string(), funding_txo.index);
                match self.write(
                        CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE,
@@ -251,36 +231,32 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner, K: KVStore> Persist<ChannelSign
                        Err(_) => chain::ChannelMonitorUpdateStatus::UnrecoverableError
                }
        }
-}
-
-impl<ChannelSigner: WriteableEcdsaChannelSigner> Persist<ChannelSigner> for dyn KVStore + Send + Sync {
-       // TODO: We really need a way for the persister to inform the user that its time to crash/shut
-       // down once these start returning failure.
-       // Then we should return InProgress rather than UnrecoverableError, implying we should probably
-       // just shut down the node since we're not retrying persistence!
 
-       fn persist_new_channel(&self, funding_txo: OutPoint, monitor: &ChannelMonitor<ChannelSigner>, _update_id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus {
-               let key = format!("{}_{}", funding_txo.txid.to_string(), funding_txo.index);
-               match self.write(
+       fn archive_persisted_channel(&self, funding_txo: OutPoint) {
+               let monitor_name = MonitorName::from(funding_txo);
+               let monitor = match self.read(
                        CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE,
                        CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE,
-                       &key, &monitor.encode())
-               {
-                       Ok(()) => chain::ChannelMonitorUpdateStatus::Completed,
-                       Err(_) => chain::ChannelMonitorUpdateStatus::UnrecoverableError
-               }
-       }
-
-       fn update_persisted_channel(&self, funding_txo: OutPoint, _update: Option<&ChannelMonitorUpdate>, monitor: &ChannelMonitor<ChannelSigner>, _update_id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus {
-               let key = format!("{}_{}", funding_txo.txid.to_string(), funding_txo.index);
+                       monitor_name.as_str(),
+               ) {
+                       Ok(monitor) => monitor,
+                       Err(_) => return
+               };
                match self.write(
+                       ARCHIVED_CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE,
+                       ARCHIVED_CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE,
+                       monitor_name.as_str(),
+                       &monitor,
+               ) {
+                       Ok(()) => {}
+                       Err(_e) => return
+               };
+               let _ = self.remove(
                        CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE,
                        CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE,
-                       &key, &monitor.encode())
-               {
-                       Ok(()) => chain::ChannelMonitorUpdateStatus::Completed,
-                       Err(_) => chain::ChannelMonitorUpdateStatus::UnrecoverableError
-               }
+                       monitor_name.as_str(),
+                       true,
+               );
        }
 }
 
@@ -661,7 +637,7 @@ where
        }
 }
 
-impl<ChannelSigner: WriteableEcdsaChannelSigner, K: Deref, L: Deref, ES: Deref, SP: Deref>
+impl<ChannelSigner: EcdsaChannelSigner, K: Deref, L: Deref, ES: Deref, SP: Deref>
        Persist<ChannelSigner> for MonitorUpdatingPersister<K, L, ES, SP>
 where
        K::Target: KVStore,
@@ -672,8 +648,7 @@ where
        /// Persists a new channel. This means writing the entire monitor to the
        /// parametrized [`KVStore`].
        fn persist_new_channel(
-               &self, funding_txo: OutPoint, monitor: &ChannelMonitor<ChannelSigner>,
-               _monitor_update_call_id: MonitorUpdateId,
+               &self, funding_txo: OutPoint, monitor: &ChannelMonitor<ChannelSigner>
        ) -> chain::ChannelMonitorUpdateStatus {
                // Determine the proper key for this monitor
                let monitor_name = MonitorName::from(funding_txo);
@@ -717,10 +692,8 @@ where
        ///   - The update is at [`CLOSED_CHANNEL_UPDATE_ID`]
        fn update_persisted_channel(
                &self, funding_txo: OutPoint, update: Option<&ChannelMonitorUpdate>,
-               monitor: &ChannelMonitor<ChannelSigner>, monitor_update_call_id: MonitorUpdateId,
+               monitor: &ChannelMonitor<ChannelSigner>
        ) -> chain::ChannelMonitorUpdateStatus {
-               // IMPORTANT: monitor_update_call_id: MonitorUpdateId is not to be confused with
-               // ChannelMonitorUpdate's update_id.
                if let Some(update) = update {
                        if update.update_id != CLOSED_CHANNEL_UPDATE_ID
                                && update.update_id % self.maximum_pending_updates != 0
@@ -756,7 +729,7 @@ where
                                };
 
                                // We could write this update, but it meets criteria of our design that calls for a full monitor write.
-                               let monitor_update_status = self.persist_new_channel(funding_txo, monitor, monitor_update_call_id);
+                               let monitor_update_status = self.persist_new_channel(funding_txo, monitor);
 
                                if let chain::ChannelMonitorUpdateStatus::Completed = monitor_update_status {
                                        let cleanup_range = if monitor.get_latest_update_id() == CLOSED_CHANNEL_UPDATE_ID {
@@ -785,9 +758,32 @@ where
                        }
                } else {
                        // There is no update given, so we must persist a new monitor.
-                       self.persist_new_channel(funding_txo, monitor, monitor_update_call_id)
+                       self.persist_new_channel(funding_txo, monitor)
                }
        }
+
+       fn archive_persisted_channel(&self, funding_txo: OutPoint) {
+               let monitor_name = MonitorName::from(funding_txo);
+               let monitor = match self.read_monitor(&monitor_name) {
+                       Ok((_block_hash, monitor)) => monitor,
+                       Err(_) => return
+               };
+               match self.kv_store.write(
+                       ARCHIVED_CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE,
+                       ARCHIVED_CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE,
+                       monitor_name.as_str(),
+                       &monitor.encode()
+               ) {
+                       Ok(()) => {},
+                       Err(_e) => return,
+               };
+               let _ = self.kv_store.remove(
+                       CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE,
+                       CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE,
+                       monitor_name.as_str(),
+                       true,
+               );
+       }
 }
 
 impl<K: Deref, L: Deref, ES: Deref, SP: Deref> MonitorUpdatingPersister<K, L, ES, SP>
@@ -905,12 +901,13 @@ impl From<u64> for UpdateName {
 #[cfg(test)]
 mod tests {
        use super::*;
-       use crate::chain::chainmonitor::Persist;
        use crate::chain::ChannelMonitorUpdateStatus;
        use crate::events::{ClosureReason, MessageSendEventsProvider};
        use crate::ln::functional_test_utils::*;
        use crate::util::test_utils::{self, TestLogger, TestStore};
        use crate::{check_added_monitors, check_closed_broadcast};
+       use crate::sync::Arc;
+       use crate::util::test_channel_signer::TestChannelSigner;
 
        const EXPECTED_UPDATES_PER_PAYMENT: u64 = 5;
 
@@ -1117,8 +1114,6 @@ mod tests {
                check_closed_event(&nodes[1], 1, ClosureReason::HolderForceClosed, false, &[nodes[0].node.get_our_node_id()], 100000);
                {
                        let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
-                       let update_map = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap();
-                       let update_id = update_map.get(&added_monitors[0].1.channel_id()).unwrap();
                        let cmu_map = nodes[1].chain_monitor.monitor_updates.lock().unwrap();
                        let cmu = &cmu_map.get(&added_monitors[0].1.channel_id()).unwrap()[0];
                        let test_txo = OutPoint { txid: Txid::from_str("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), index: 0 };
@@ -1130,7 +1125,7 @@ mod tests {
                                entropy_source: node_cfgs[0].keys_manager,
                                signer_provider: node_cfgs[0].keys_manager,
                        };
-                       match ro_persister.persist_new_channel(test_txo, &added_monitors[0].1, update_id.2) {
+                       match ro_persister.persist_new_channel(test_txo, &added_monitors[0].1) {
                                ChannelMonitorUpdateStatus::UnrecoverableError => {
                                        // correct result
                                }
@@ -1141,7 +1136,7 @@ mod tests {
                                        panic!("Returned InProgress when shouldn't have")
                                }
                        }
-                       match ro_persister.update_persisted_channel(test_txo, Some(cmu), &added_monitors[0].1, update_id.2) {
+                       match ro_persister.update_persisted_channel(test_txo, Some(cmu), &added_monitors[0].1) {
                                ChannelMonitorUpdateStatus::UnrecoverableError => {
                                        // correct result
                                }
@@ -1251,4 +1246,14 @@ mod tests {
                        .read(CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, monitor_name.as_str(), UpdateName::from(u64::MAX - 1).as_str())
                        .is_err());
        }
+
+       fn persist_fn<P: Deref, ChannelSigner: EcdsaChannelSigner>(_persist: P) -> bool where P::Target: Persist<ChannelSigner> {
+               true
+       }
+
+       #[test]
+       fn kvstore_trait_object_usage() {
+               let store: Arc<dyn KVStore + Send + Sync> = Arc::new(TestStore::new(false));
+               assert!(persist_fn::<_, TestChannelSigner>(store.clone()));
+       }
 }
index 38be0eb88fc3bdc0767c6725e30fb7437f3bbec4..ce789ef9fdc6fd7eb2b23f04f7c66ff379238ac5 100644 (file)
@@ -72,12 +72,12 @@ pub fn scid_from_parts(block: u64, tx_index: u64, vout_index: u64) -> Result<u64
 ///    the forwarding node can open a JIT channel to the next hop)
 pub(crate) mod fake_scid {
        use bitcoin::blockdata::constants::ChainHash;
-       use bitcoin::network::constants::Network;
+       use bitcoin::Network;
        use crate::sign::EntropySource;
        use crate::crypto::chacha20::ChaCha20;
        use crate::util::scid_utils;
+       use crate::prelude::*;
 
-       use core::convert::TryInto;
        use core::ops::Deref;
 
        const TEST_SEGWIT_ACTIVATION_HEIGHT: u32 = 1;
@@ -183,7 +183,7 @@ pub(crate) mod fake_scid {
        #[cfg(test)]
        mod tests {
                use bitcoin::blockdata::constants::ChainHash;
-               use bitcoin::network::constants::Network;
+               use bitcoin::network::Network;
                use crate::util::scid_utils::fake_scid::{is_valid_intercept, is_valid_phantom, MAINNET_SEGWIT_ACTIVATION_HEIGHT, MAX_TX_INDEX, MAX_NAMESPACES, Namespace, NAMESPACE_ID_BITMASK, segwit_activation_height, TEST_SEGWIT_ACTIVATION_HEIGHT};
                use crate::util::scid_utils;
                use crate::util::test_utils;
index 7447c92d4be8b61abc23b0527d458796687bd29e..5f109e1a93c64529d09e462899fb539abf662c68 100644 (file)
@@ -19,7 +19,6 @@ use crate::io_extras::{copy, sink};
 use core::hash::Hash;
 use crate::sync::{Mutex, RwLock};
 use core::cmp;
-use core::convert::TryFrom;
 use core::ops::Deref;
 
 use alloc::collections::BTreeMap;
@@ -28,6 +27,7 @@ use bitcoin::secp256k1::{PublicKey, SecretKey};
 use bitcoin::secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, COMPACT_SIGNATURE_SIZE, SCHNORR_SIGNATURE_SIZE};
 use bitcoin::secp256k1::ecdsa;
 use bitcoin::secp256k1::schnorr;
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::constants::ChainHash;
 use bitcoin::blockdata::script::{self, ScriptBuf};
 use bitcoin::blockdata::transaction::{OutPoint, Transaction, TxOut};
@@ -35,13 +35,12 @@ use bitcoin::{consensus, Witness};
 use bitcoin::consensus::Encodable;
 use bitcoin::hashes::sha256d::Hash as Sha256dHash;
 use bitcoin::hash_types::{Txid, BlockHash};
-use core::marker::Sized;
 use core::time::Duration;
 use crate::chain::ClaimId;
 use crate::ln::msgs::DecodeError;
 #[cfg(taproot)]
 use crate::ln::msgs::PartialSignatureWithNonce;
-use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
+use crate::ln::types::{PaymentPreimage, PaymentHash, PaymentSecret};
 
 use crate::util::byte_utils::{be48_to_array, slice_to_be48};
 use crate::util::string::UntrustedString;
@@ -108,14 +107,14 @@ impl Writer for LengthCalculatingWriter {
 /// forward to ensure we always consume exactly the fixed length specified.
 ///
 /// This is not exported to bindings users as manual TLV building is not currently supported in bindings
-pub struct FixedLengthReader<R: Read> {
-       read: R,
+pub struct FixedLengthReader<'a, R: Read> {
+       read: &'a mut R,
        bytes_read: u64,
        total_bytes: u64,
 }
-impl<R: Read> FixedLengthReader<R> {
+impl<'a, R: Read> FixedLengthReader<'a, R> {
        /// Returns a new [`FixedLengthReader`].
-       pub fn new(read: R, total_bytes: u64) -> Self {
+       pub fn new(read: &'a mut R, total_bytes: u64) -> Self {
                Self { read, bytes_read: 0, total_bytes }
        }
 
@@ -136,7 +135,7 @@ impl<R: Read> FixedLengthReader<R> {
                }
        }
 }
-impl<R: Read> Read for FixedLengthReader<R> {
+impl<'a, R: Read> Read for FixedLengthReader<'a, R> {
        #[inline]
        fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
                if self.total_bytes == self.bytes_read {
@@ -154,7 +153,7 @@ impl<R: Read> Read for FixedLengthReader<R> {
        }
 }
 
-impl<R: Read> LengthRead for FixedLengthReader<R> {
+impl<'a, R: Read> LengthRead for FixedLengthReader<'a, R> {
        #[inline]
        fn total_bytes(&self) -> u64 {
                self.total_bytes
@@ -820,6 +819,49 @@ macro_rules! impl_for_vec {
        }
 }
 
+// Alternatives to impl_writeable_for_vec/impl_readable_for_vec that add a length prefix to each
+// element in the Vec. Intended to be used when elements have variable lengths.
+macro_rules! impl_writeable_for_vec_with_element_length_prefix {
+       ($ty: ty $(, $name: ident)*) => {
+               impl<$($name : Writeable),*> Writeable for Vec<$ty> {
+                       #[inline]
+                       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+                               CollectionLength(self.len() as u64).write(w)?;
+                               for elem in self.iter() {
+                                       CollectionLength(elem.serialized_length() as u64).write(w)?;
+                                       elem.write(w)?;
+                               }
+                               Ok(())
+                       }
+               }
+       }
+}
+macro_rules! impl_readable_for_vec_with_element_length_prefix {
+       ($ty: ty $(, $name: ident)*) => {
+               impl<$($name : Readable),*> Readable for Vec<$ty> {
+                       #[inline]
+                       fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+                               let len: CollectionLength = Readable::read(r)?;
+                               let mut ret = Vec::with_capacity(cmp::min(len.0 as usize, MAX_BUF_SIZE / core::mem::size_of::<$ty>()));
+                               for _ in 0..len.0 {
+                                       let elem_len: CollectionLength = Readable::read(r)?;
+                                       let mut elem_reader = FixedLengthReader::new(r, elem_len.0);
+                                       if let Some(val) = MaybeReadable::read(&mut elem_reader)? {
+                                               ret.push(val);
+                                       }
+                               }
+                               Ok(ret)
+                       }
+               }
+       }
+}
+macro_rules! impl_for_vec_with_element_length_prefix {
+       ($ty: ty $(, $name: ident)*) => {
+               impl_writeable_for_vec_with_element_length_prefix!($ty $(, $name)*);
+               impl_readable_for_vec_with_element_length_prefix!($ty $(, $name)*);
+       }
+}
+
 impl Writeable for Vec<u8> {
        #[inline]
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
@@ -851,13 +893,15 @@ impl_for_vec!(crate::ln::msgs::SocketAddress);
 impl_for_vec!((A, B), A, B);
 impl_writeable_for_vec!(&crate::routing::router::BlindedTail);
 impl_readable_for_vec!(crate::routing::router::BlindedTail);
+impl_for_vec_with_element_length_prefix!(crate::ln::msgs::UpdateAddHTLC);
+impl_writeable_for_vec_with_element_length_prefix!(&crate::ln::msgs::UpdateAddHTLC);
 
 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.size() as u16).write(w)?;
                        witness.write(w)?;
                }
                Ok(())
@@ -877,7 +921,7 @@ impl Readable for Vec<Witness> {
                        // 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 {
+                       if witness.size() != witness_len {
                                return Err(DecodeError::BadLengthDescriptor);
                        }
                        witnesses.push(witness);
@@ -1102,6 +1146,20 @@ impl<T: Readable> Readable for Option<T>
        }
 }
 
+impl Writeable for Amount {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               self.to_sat().write(w)
+       }
+}
+
+
+impl Readable for Amount {
+       fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+               let amount: u64 = Readable::read(r)?;
+               Ok(Amount::from_sat(amount))
+       }
+}
+
 impl Writeable for Txid {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
                w.write_all(&self[..])
@@ -1408,6 +1466,11 @@ impl TransactionU16LenLimited {
        pub fn into_transaction(self) -> Transaction {
                self.0
        }
+
+       /// Returns a reference to the contained `Transaction`
+       pub fn as_transaction(&self) -> &Transaction {
+               &self.0
+       }
 }
 
 impl Writeable for TransactionU16LenLimited {
@@ -1444,10 +1507,10 @@ impl Readable for ClaimId {
 
 #[cfg(test)]
 mod tests {
-       use core::convert::TryFrom;
        use bitcoin::hashes::hex::FromHex;
        use bitcoin::secp256k1::ecdsa;
        use crate::util::ser::{Readable, Hostname, Writeable};
+       use crate::prelude::*;
 
        #[test]
        fn hostname_conversion() {
index 84d9f7a180bb301a1c18a136530a04ce75589a9e..740b7c12561ce8466d1a2e30f56faacc797961ac 100644 (file)
@@ -354,25 +354,25 @@ macro_rules! _check_missing_tlv {
 #[doc(hidden)]
 #[macro_export]
 macro_rules! _decode_tlv {
-       ($reader: expr, $field: ident, (default_value, $default: expr)) => {{
-               $crate::_decode_tlv!($reader, $field, required)
+       ($outer_reader: expr, $reader: expr, $field: ident, (default_value, $default: expr)) => {{
+               $crate::_decode_tlv!($outer_reader, $reader, $field, required)
        }};
-       ($reader: expr, $field: ident, (static_value, $value: expr)) => {{
+       ($outer_reader: expr, $reader: expr, $field: ident, (static_value, $value: expr)) => {{
        }};
-       ($reader: expr, $field: ident, required) => {{
+       ($outer_reader: expr, $reader: expr, $field: ident, required) => {{
                $field = $crate::util::ser::Readable::read(&mut $reader)?;
        }};
-       ($reader: expr, $field: ident, (required: $trait: ident $(, $read_arg: expr)?)) => {{
+       ($outer_reader: expr, $reader: expr, $field: ident, (required: $trait: ident $(, $read_arg: expr)?)) => {{
                $field = $trait::read(&mut $reader $(, $read_arg)*)?;
        }};
-       ($reader: expr, $field: ident, required_vec) => {{
+       ($outer_reader: expr, $reader: expr, $field: ident, required_vec) => {{
                let f: $crate::util::ser::WithoutLength<Vec<_>> = $crate::util::ser::Readable::read(&mut $reader)?;
                $field = f.0;
        }};
-       ($reader: expr, $field: ident, option) => {{
+       ($outer_reader: expr, $reader: expr, $field: ident, option) => {{
                $field = Some($crate::util::ser::Readable::read(&mut $reader)?);
        }};
-       ($reader: expr, $field: ident, optional_vec) => {{
+       ($outer_reader: expr, $reader: expr, $field: ident, optional_vec) => {{
                let f: $crate::util::ser::WithoutLength<Vec<_>> = $crate::util::ser::Readable::read(&mut $reader)?;
                $field = Some(f.0);
        }};
@@ -380,32 +380,52 @@ macro_rules! _decode_tlv {
        // without backwards compat. We'll error if the field is missing, and return `Ok(None)` if the
        // field is present but we can no longer understand it.
        // Note that this variant can only be used within a `MaybeReadable` read.
-       ($reader: expr, $field: ident, upgradable_required) => {{
+       ($outer_reader: expr, $reader: expr, $field: ident, upgradable_required) => {{
                $field = match $crate::util::ser::MaybeReadable::read(&mut $reader)? {
                        Some(res) => res,
-                       _ => return Ok(None)
+                       None => {
+                               // If we successfully read a value but we don't know how to parse it, we give up
+                               // and immediately return `None`. However, we need to make sure we read the correct
+                               // number of bytes for this TLV stream, which is implicitly the end of the stream.
+                               // Thus, we consume everything left in the `$outer_reader` here, ensuring that if
+                               // we're being read as a part of another TLV stream we don't spuriously fail to
+                               // deserialize the outer object due to a TLV length mismatch.
+                               $crate::io_extras::copy($outer_reader, &mut $crate::io_extras::sink()).unwrap();
+                               return Ok(None)
+                       },
                };
        }};
        // `upgradable_option` indicates we're reading an Option-al TLV that may have been upgraded
        // without backwards compat. $field will be None if the TLV is missing or if the field is present
        // but we can no longer understand it.
-       ($reader: expr, $field: ident, upgradable_option) => {{
+       ($outer_reader: expr, $reader: expr, $field: ident, upgradable_option) => {{
                $field = $crate::util::ser::MaybeReadable::read(&mut $reader)?;
+               if $field.is_none() {
+                       #[cfg(not(debug_assertions))] {
+                               // In general, MaybeReadable implementations are required to consume all the bytes
+                               // of the object even if they don't understand it, but due to a bug in the
+                               // serialization format for `impl_writeable_tlv_based_enum_upgradable` we sometimes
+                               // don't know how many bytes that is. In such cases, we'd like to spuriously allow
+                               // TLV length mismatches, which we do here by calling `eat_remaining` so that the
+                               // `s.bytes_remain()` check in `_decode_tlv_stream_range` doesn't fail.
+                               $reader.eat_remaining()?;
+                       }
+               }
        }};
-       ($reader: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
+       ($outer_reader: expr, $reader: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
                $field = Some($trait::read(&mut $reader $(, $read_arg)*)?);
        }};
-       ($reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident, $encoder:ty))) => {{
-               $crate::_decode_tlv!($reader, $field, (option, encoding: ($fieldty, $encoding)));
+       ($outer_reader: expr, $reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident, $encoder:ty))) => {{
+               $crate::_decode_tlv!($outer_reader, $reader, $field, (option, encoding: ($fieldty, $encoding)));
        }};
-       ($reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident))) => {{
+       ($outer_reader: expr, $reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident))) => {{
                $field = {
                        let field: $encoding<$fieldty> = ser::Readable::read(&mut $reader)?;
                        Some(field.0)
                };
        }};
-       ($reader: expr, $field: ident, (option, encoding: $fieldty: ty)) => {{
-               $crate::_decode_tlv!($reader, $field, option);
+       ($outer_reader: expr, $reader: expr, $field: ident, (option, encoding: $fieldty: ty)) => {{
+               $crate::_decode_tlv!($outer_reader, $reader, $field, option);
        }};
 }
 
@@ -539,7 +559,7 @@ macro_rules! _decode_tlv_stream_range {
                        let mut s = ser::FixedLengthReader::new(&mut stream_ref, length.0);
                        match typ.0 {
                                $(_t if $crate::_decode_tlv_stream_match_check!(_t, $type, $fieldty) => {
-                                       $crate::_decode_tlv!(s, $field, $fieldty);
+                                       $crate::_decode_tlv!($stream, s, $field, $fieldty);
                                        if s.bytes_remain() {
                                                s.eat_remaining()?; // Return ShortRead if there's actually not enough bytes
                                                return Err(DecodeError::InvalidValue);
@@ -1033,7 +1053,7 @@ macro_rules! impl_writeable_tlv_based_enum {
                                        $($variant_id => {
                                                // Because read_tlv_fields creates a labeled loop, we cannot call it twice
                                                // in the same function body. Instead, we define a closure and call it.
-                                               let f = || {
+                                               let mut f = || {
                                                        $crate::_init_and_read_len_prefixed_tlv_fields!(reader, {
                                                                $(($type, $field, $fieldty)),*
                                                        });
@@ -1065,6 +1085,10 @@ macro_rules! impl_writeable_tlv_based_enum {
 /// when [`MaybeReadable`] is practical instead of just [`Readable`] as it provides an upgrade path for
 /// new variants to be added which are simply ignored by existing clients.
 ///
+/// Note that only struct and unit variants (not tuple variants) will support downgrading, thus any
+/// new odd variants MUST be non-tuple (i.e. described using `$variant_id` and `$variant_name` not
+/// `$tuple_variant_id` and `$tuple_variant_name`).
+///
 /// [`MaybeReadable`]: crate::util::ser::MaybeReadable
 /// [`Writeable`]: crate::util::ser::Writeable
 /// [`DecodeError::UnknownRequiredFeature`]: crate::ln::msgs::DecodeError::UnknownRequiredFeature
@@ -1087,7 +1111,7 @@ macro_rules! impl_writeable_tlv_based_enum_upgradable {
                                        $($variant_id => {
                                                // Because read_tlv_fields creates a labeled loop, we cannot call it twice
                                                // in the same function body. Instead, we define a closure and call it.
-                                               let f = || {
+                                               let mut f = || {
                                                        $crate::_init_and_read_len_prefixed_tlv_fields!(reader, {
                                                                $(($type, $field, $fieldty)),*
                                                        });
@@ -1102,7 +1126,14 @@ macro_rules! impl_writeable_tlv_based_enum_upgradable {
                                        $($($tuple_variant_id => {
                                                Ok(Some($st::$tuple_variant_name(Readable::read(reader)?)))
                                        }),*)*
-                                       _ if id % 2 == 1 => Ok(None),
+                                       _ if id % 2 == 1 => {
+                                               // Assume that a $variant_id was written, not a $tuple_variant_id, and read
+                                               // the length prefix and discard the correct number of bytes.
+                                               let tlv_len: $crate::util::ser::BigSize = $crate::util::ser::Readable::read(reader)?;
+                                               let mut rd = $crate::util::ser::FixedLengthReader::new(reader, tlv_len.0);
+                                               rd.eat_remaining().map_err(|_| $crate::ln::msgs::DecodeError::ShortRead)?;
+                                               Ok(None)
+                                       },
                                        _ => Err($crate::ln::msgs::DecodeError::UnknownRequiredFeature),
                                }
                        }
@@ -1112,10 +1143,12 @@ macro_rules! impl_writeable_tlv_based_enum_upgradable {
 
 #[cfg(test)]
 mod tests {
-       use crate::io::{self, Cursor};
+       #[allow(unused_imports)]
        use crate::prelude::*;
+
+       use crate::io::{self, Cursor};
        use crate::ln::msgs::DecodeError;
-       use crate::util::ser::{Writeable, HighZeroBytesDroppedBigSize, VecWriter};
+       use crate::util::ser::{MaybeReadable, Readable, Writeable, HighZeroBytesDroppedBigSize, VecWriter};
        use bitcoin::hashes::hex::FromHex;
        use bitcoin::secp256k1::PublicKey;
 
@@ -1225,6 +1258,131 @@ mod tests {
                } else { panic!(); }
        }
 
+       /// A "V1" enum with only one variant
+       enum InnerEnumV1 {
+               StructVariantA {
+                       field: u32,
+               },
+       }
+
+       impl_writeable_tlv_based_enum_upgradable!(InnerEnumV1,
+               (0, StructVariantA) => {
+                       (0, field, required),
+               },
+       );
+
+       struct OuterStructOptionalEnumV1 {
+               inner_enum: Option<InnerEnumV1>,
+               other_field: u32,
+       }
+
+       impl_writeable_tlv_based!(OuterStructOptionalEnumV1, {
+               (0, inner_enum, upgradable_option),
+               (2, other_field, required),
+       });
+
+       /// An upgraded version of [`InnerEnumV1`] that added a second variant
+       enum InnerEnumV2 {
+               StructVariantA {
+                       field: u32,
+               },
+               StructVariantB {
+                       field2: u64,
+               }
+       }
+
+       impl_writeable_tlv_based_enum_upgradable!(InnerEnumV2,
+               (0, StructVariantA) => {
+                       (0, field, required),
+               },
+               (1, StructVariantB) => {
+                       (0, field2, required),
+               },
+       );
+
+       struct OuterStructOptionalEnumV2 {
+               inner_enum: Option<InnerEnumV2>,
+               other_field: u32,
+       }
+
+       impl_writeable_tlv_based!(OuterStructOptionalEnumV2, {
+               (0, inner_enum, upgradable_option),
+               (2, other_field, required),
+       });
+
+       #[test]
+       fn upgradable_enum_option() {
+               // Test downgrading from `OuterStructOptionalEnumV2` to `OuterStructOptionalEnumV1` and
+               // ensure we still read the `other_field` just fine.
+               let serialized_bytes = OuterStructOptionalEnumV2 {
+                       inner_enum: Some(InnerEnumV2::StructVariantB { field2: 64 }),
+                       other_field: 0x1bad1dea,
+               }.encode();
+               let mut s = Cursor::new(serialized_bytes);
+
+               let outer_struct: OuterStructOptionalEnumV1 = Readable::read(&mut s).unwrap();
+               assert!(outer_struct.inner_enum.is_none());
+               assert_eq!(outer_struct.other_field, 0x1bad1dea);
+       }
+
+       /// A struct that is read with an [`InnerEnumV1`] but is written with an [`InnerEnumV2`].
+       struct OuterStructRequiredEnum {
+               #[allow(unused)]
+               inner_enum: InnerEnumV1,
+       }
+
+       impl MaybeReadable for OuterStructRequiredEnum {
+               fn read<R: io::Read>(reader: &mut R) -> Result<Option<Self>, DecodeError> {
+                       let mut inner_enum = crate::util::ser::UpgradableRequired(None);
+                       read_tlv_fields!(reader, {
+                               (0, inner_enum, upgradable_required),
+                       });
+                       Ok(Some(Self {
+                               inner_enum: inner_enum.0.unwrap(),
+                       }))
+               }
+       }
+
+       impl Writeable for OuterStructRequiredEnum {
+               fn write<W: crate::util::ser::Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+                       write_tlv_fields!(writer, {
+                               (0, InnerEnumV2::StructVariantB { field2: 0xdeadbeef }, required),
+                       });
+                       Ok(())
+               }
+       }
+
+       struct OuterOuterStruct {
+               outer_struct: Option<OuterStructRequiredEnum>,
+               other_field: u32,
+       }
+
+       impl_writeable_tlv_based!(OuterOuterStruct, {
+               (0, outer_struct, upgradable_option),
+               (2, other_field, required),
+       });
+
+
+       #[test]
+       fn upgradable_enum_required() {
+               // Test downgrading from an `OuterOuterStruct` (i.e. test downgrading an
+               // `upgradable_required` `InnerEnumV2` to an `InnerEnumV1`).
+               //
+               // Note that `OuterStructRequiredEnum` has a split write/read implementation that writes an
+               // `InnerEnumV2::StructVariantB` irrespective of the value of `inner_enum`.
+
+               let dummy_inner_enum = InnerEnumV1::StructVariantA { field: 42 };
+               let serialized_bytes = OuterOuterStruct {
+                       outer_struct: Some(OuterStructRequiredEnum { inner_enum: dummy_inner_enum }),
+                       other_field: 0x1bad1dea,
+               }.encode();
+               let mut s = Cursor::new(serialized_bytes);
+
+               let outer_outer_struct: OuterOuterStruct = Readable::read(&mut s).unwrap();
+               assert!(outer_outer_struct.outer_struct.is_none());
+               assert_eq!(outer_outer_struct.other_field, 0x1bad1dea);
+       }
+
        // BOLT TLV test cases
        fn tlv_reader_n1(s: &[u8]) -> Result<(Option<HighZeroBytesDroppedBigSize<u64>>, Option<u64>, Option<(PublicKey, u64, u64)>, Option<u16>), DecodeError> {
                let mut s = Cursor::new(s);
index 6949c936e00e575ad8b4a848e61ee892a65b3eca..ab12486a0d8ebfbac985dd3289b8e6dbeb23f3f2 100644 (file)
@@ -9,12 +9,14 @@
 
 //! Utilities for strings.
 
-use alloc::string::String;
 use core::fmt;
 use crate::io::{self, Read};
 use crate::ln::msgs;
 use crate::util::ser::{Writeable, Writer, Readable};
 
+#[allow(unused_imports)]
+use crate::prelude::*;
+
 /// Struct to `Display` fields in a safe way using `PrintableString`
 #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
 pub struct UntrustedString(pub String);
diff --git a/lightning/src/util/sweep.rs b/lightning/src/util/sweep.rs
new file mode 100644 (file)
index 0000000..dd26a8e
--- /dev/null
@@ -0,0 +1,877 @@
+// 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 module contains an [`OutputSweeper`] utility that keeps track of
+//! [`SpendableOutputDescriptor`]s, i.e., persists them in a given [`KVStore`] and regularly retries
+//! sweeping them.
+
+use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
+use crate::chain::channelmonitor::ANTI_REORG_DELAY;
+use crate::chain::{self, BestBlock, Confirm, Filter, Listen, WatchedOutput};
+use crate::io;
+use crate::ln::msgs::DecodeError;
+use crate::ln::types::ChannelId;
+use crate::prelude::*;
+use crate::sign::{ChangeDestinationSource, OutputSpender, SpendableOutputDescriptor};
+use crate::sync::Mutex;
+use crate::util::logger::Logger;
+use crate::util::persist::{
+       KVStore, OUTPUT_SWEEPER_PERSISTENCE_KEY, OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE,
+       OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE,
+};
+use crate::util::ser::{Readable, ReadableArgs, Writeable};
+use crate::{impl_writeable_tlv_based, log_debug, log_error};
+
+use bitcoin::blockdata::block::Header;
+use bitcoin::blockdata::locktime::absolute::LockTime;
+use bitcoin::secp256k1::Secp256k1;
+use bitcoin::{BlockHash, Transaction, Txid};
+
+use core::ops::Deref;
+
+/// The state of a spendable output currently tracked by an [`OutputSweeper`].
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TrackedSpendableOutput {
+       /// The tracked output descriptor.
+       pub descriptor: SpendableOutputDescriptor,
+       /// The channel this output belongs to.
+       ///
+       /// Will be `None` if no `channel_id` was given to [`OutputSweeper::track_spendable_outputs`]
+       pub channel_id: Option<ChannelId>,
+       /// The current status of the output spend.
+       pub status: OutputSpendStatus,
+}
+
+impl TrackedSpendableOutput {
+       fn to_watched_output(&self, cur_hash: BlockHash) -> WatchedOutput {
+               let block_hash = self.status.first_broadcast_hash().or(Some(cur_hash));
+               match &self.descriptor {
+                       SpendableOutputDescriptor::StaticOutput { outpoint, output, channel_keys_id: _ } => {
+                               WatchedOutput {
+                                       block_hash,
+                                       outpoint: *outpoint,
+                                       script_pubkey: output.script_pubkey.clone(),
+                               }
+                       },
+                       SpendableOutputDescriptor::DelayedPaymentOutput(output) => WatchedOutput {
+                               block_hash,
+                               outpoint: output.outpoint,
+                               script_pubkey: output.output.script_pubkey.clone(),
+                       },
+                       SpendableOutputDescriptor::StaticPaymentOutput(output) => WatchedOutput {
+                               block_hash,
+                               outpoint: output.outpoint,
+                               script_pubkey: output.output.script_pubkey.clone(),
+                       },
+               }
+       }
+
+       /// Returns whether the output is spent in the given transaction.
+       pub fn is_spent_in(&self, tx: &Transaction) -> bool {
+               let prev_outpoint = match &self.descriptor {
+                       SpendableOutputDescriptor::StaticOutput { outpoint, .. } => *outpoint,
+                       SpendableOutputDescriptor::DelayedPaymentOutput(output) => output.outpoint,
+                       SpendableOutputDescriptor::StaticPaymentOutput(output) => output.outpoint,
+               }
+               .into_bitcoin_outpoint();
+
+               tx.input.iter().any(|input| input.previous_output == prev_outpoint)
+       }
+}
+
+impl_writeable_tlv_based!(TrackedSpendableOutput, {
+       (0, descriptor, required),
+       (2, channel_id, option),
+       (4, status, required),
+});
+
+/// The current status of the output spend.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum OutputSpendStatus {
+       /// The output is tracked but an initial spending transaction hasn't been generated and
+       /// broadcasted yet.
+       PendingInitialBroadcast {
+               /// The height at which we will first generate and broadcast a spending transaction.
+               delayed_until_height: Option<u32>,
+       },
+       /// A transaction spending the output has been broadcasted but is pending its first confirmation on-chain.
+       PendingFirstConfirmation {
+               /// The hash of the chain tip when we first broadcast a transaction spending this output.
+               first_broadcast_hash: BlockHash,
+               /// The best height when we last broadcast a transaction spending this output.
+               latest_broadcast_height: u32,
+               /// The transaction spending this output we last broadcasted.
+               latest_spending_tx: Transaction,
+       },
+       /// A transaction spending the output has been confirmed on-chain but will be tracked until it
+       /// reaches [`ANTI_REORG_DELAY`] confirmations.
+       PendingThresholdConfirmations {
+               /// The hash of the chain tip when we first broadcast a transaction spending this output.
+               first_broadcast_hash: BlockHash,
+               /// The best height when we last broadcast a transaction spending this output.
+               latest_broadcast_height: u32,
+               /// The transaction spending this output we saw confirmed on-chain.
+               latest_spending_tx: Transaction,
+               /// The height at which the spending transaction was confirmed.
+               confirmation_height: u32,
+               /// The hash of the block in which the spending transaction was confirmed.
+               confirmation_hash: BlockHash,
+       },
+}
+
+impl OutputSpendStatus {
+       fn broadcast(&mut self, cur_hash: BlockHash, cur_height: u32, latest_spending_tx: Transaction) {
+               match self {
+                       Self::PendingInitialBroadcast { delayed_until_height } => {
+                               if let Some(delayed_until_height) = delayed_until_height {
+                                       debug_assert!(
+                                               cur_height >= *delayed_until_height,
+                                               "We should never broadcast before the required height is reached."
+                                       );
+                               }
+                               *self = Self::PendingFirstConfirmation {
+                                       first_broadcast_hash: cur_hash,
+                                       latest_broadcast_height: cur_height,
+                                       latest_spending_tx,
+                               };
+                       },
+                       Self::PendingFirstConfirmation { first_broadcast_hash, .. } => {
+                               *self = Self::PendingFirstConfirmation {
+                                       first_broadcast_hash: *first_broadcast_hash,
+                                       latest_broadcast_height: cur_height,
+                                       latest_spending_tx,
+                               };
+                       },
+                       Self::PendingThresholdConfirmations { .. } => {
+                               debug_assert!(false, "We should never rebroadcast confirmed transactions.");
+                       },
+               }
+       }
+
+       fn confirmed(
+               &mut self, confirmation_hash: BlockHash, confirmation_height: u32,
+               latest_spending_tx: Transaction,
+       ) {
+               match self {
+                       Self::PendingInitialBroadcast { .. } => {
+                               // Generally we can't see any of our transactions confirmed if they haven't been
+                               // broadcasted yet, so this should never be reachable via `transactions_confirmed`.
+                               debug_assert!(false, "We should never confirm when we haven't broadcasted. This a bug and should never happen, please report.");
+                               *self = Self::PendingThresholdConfirmations {
+                                       first_broadcast_hash: confirmation_hash,
+                                       latest_broadcast_height: confirmation_height,
+                                       latest_spending_tx,
+                                       confirmation_height,
+                                       confirmation_hash,
+                               };
+                       },
+                       Self::PendingFirstConfirmation {
+                               first_broadcast_hash,
+                               latest_broadcast_height,
+                               ..
+                       } => {
+                               debug_assert!(confirmation_height >= *latest_broadcast_height);
+                               *self = Self::PendingThresholdConfirmations {
+                                       first_broadcast_hash: *first_broadcast_hash,
+                                       latest_broadcast_height: *latest_broadcast_height,
+                                       latest_spending_tx,
+                                       confirmation_height,
+                                       confirmation_hash,
+                               };
+                       },
+                       Self::PendingThresholdConfirmations {
+                               first_broadcast_hash,
+                               latest_broadcast_height,
+                               ..
+                       } => {
+                               *self = Self::PendingThresholdConfirmations {
+                                       first_broadcast_hash: *first_broadcast_hash,
+                                       latest_broadcast_height: *latest_broadcast_height,
+                                       latest_spending_tx,
+                                       confirmation_height,
+                                       confirmation_hash,
+                               };
+                       },
+               }
+       }
+
+       fn unconfirmed(&mut self) {
+               match self {
+                       Self::PendingInitialBroadcast { .. } => {
+                               debug_assert!(
+                                       false,
+                                       "We should only mark a spend as unconfirmed if it used to be confirmed."
+                               );
+                       },
+                       Self::PendingFirstConfirmation { .. } => {
+                               debug_assert!(
+                                       false,
+                                       "We should only mark a spend as unconfirmed if it used to be confirmed."
+                               );
+                       },
+                       Self::PendingThresholdConfirmations {
+                               first_broadcast_hash,
+                               latest_broadcast_height,
+                               latest_spending_tx,
+                               ..
+                       } => {
+                               *self = Self::PendingFirstConfirmation {
+                                       first_broadcast_hash: *first_broadcast_hash,
+                                       latest_broadcast_height: *latest_broadcast_height,
+                                       latest_spending_tx: latest_spending_tx.clone(),
+                               };
+                       },
+               }
+       }
+
+       fn is_delayed(&self, cur_height: u32) -> bool {
+               match self {
+                       Self::PendingInitialBroadcast { delayed_until_height } => {
+                               delayed_until_height.map_or(false, |req_height| cur_height < req_height)
+                       },
+                       Self::PendingFirstConfirmation { .. } => false,
+                       Self::PendingThresholdConfirmations { .. } => false,
+               }
+       }
+
+       fn first_broadcast_hash(&self) -> Option<BlockHash> {
+               match self {
+                       Self::PendingInitialBroadcast { .. } => None,
+                       Self::PendingFirstConfirmation { first_broadcast_hash, .. } => {
+                               Some(*first_broadcast_hash)
+                       },
+                       Self::PendingThresholdConfirmations { first_broadcast_hash, .. } => {
+                               Some(*first_broadcast_hash)
+                       },
+               }
+       }
+
+       fn latest_broadcast_height(&self) -> Option<u32> {
+               match self {
+                       Self::PendingInitialBroadcast { .. } => None,
+                       Self::PendingFirstConfirmation { latest_broadcast_height, .. } => {
+                               Some(*latest_broadcast_height)
+                       },
+                       Self::PendingThresholdConfirmations { latest_broadcast_height, .. } => {
+                               Some(*latest_broadcast_height)
+                       },
+               }
+       }
+
+       fn confirmation_height(&self) -> Option<u32> {
+               match self {
+                       Self::PendingInitialBroadcast { .. } => None,
+                       Self::PendingFirstConfirmation { .. } => None,
+                       Self::PendingThresholdConfirmations { confirmation_height, .. } => {
+                               Some(*confirmation_height)
+                       },
+               }
+       }
+
+       fn confirmation_hash(&self) -> Option<BlockHash> {
+               match self {
+                       Self::PendingInitialBroadcast { .. } => None,
+                       Self::PendingFirstConfirmation { .. } => None,
+                       Self::PendingThresholdConfirmations { confirmation_hash, .. } => {
+                               Some(*confirmation_hash)
+                       },
+               }
+       }
+
+       fn latest_spending_tx(&self) -> Option<&Transaction> {
+               match self {
+                       Self::PendingInitialBroadcast { .. } => None,
+                       Self::PendingFirstConfirmation { latest_spending_tx, .. } => Some(latest_spending_tx),
+                       Self::PendingThresholdConfirmations { latest_spending_tx, .. } => {
+                               Some(latest_spending_tx)
+                       },
+               }
+       }
+
+       fn is_confirmed(&self) -> bool {
+               match self {
+                       Self::PendingInitialBroadcast { .. } => false,
+                       Self::PendingFirstConfirmation { .. } => false,
+                       Self::PendingThresholdConfirmations { .. } => true,
+               }
+       }
+}
+
+impl_writeable_tlv_based_enum!(OutputSpendStatus,
+       (0, PendingInitialBroadcast) => {
+               (0, delayed_until_height, option),
+       },
+       (2, PendingFirstConfirmation) => {
+               (0, first_broadcast_hash, required),
+               (2, latest_broadcast_height, required),
+               (4, latest_spending_tx, required),
+       },
+       (4, PendingThresholdConfirmations) => {
+               (0, first_broadcast_hash, required),
+               (2, latest_broadcast_height, required),
+               (4, latest_spending_tx, required),
+               (6, confirmation_height, required),
+               (8, confirmation_hash, required),
+       };
+);
+
+/// A utility that keeps track of [`SpendableOutputDescriptor`]s, persists them in a given
+/// [`KVStore`] and regularly retries sweeping them based on a callback given to the constructor
+/// methods.
+///
+/// Users should call [`Self::track_spendable_outputs`] for any [`SpendableOutputDescriptor`]s received via [`Event::SpendableOutputs`].
+///
+/// This needs to be notified of chain state changes either via its [`Listen`] or [`Confirm`]
+/// implementation and hence has to be connected with the utilized chain data sources.
+///
+/// If chain data is provided via the [`Confirm`] interface or via filtered blocks, users are
+/// required to give their chain data sources (i.e., [`Filter`] implementation) to the respective
+/// constructor.
+///
+/// [`Event::SpendableOutputs`]: crate::events::Event::SpendableOutputs
+pub struct OutputSweeper<B: Deref, D: Deref, E: Deref, F: Deref, K: Deref, L: Deref, O: Deref>
+where
+       B::Target: BroadcasterInterface,
+       D::Target: ChangeDestinationSource,
+       E::Target: FeeEstimator,
+       F::Target: Filter + Sync + Send,
+       K::Target: KVStore,
+       L::Target: Logger,
+       O::Target: OutputSpender,
+{
+       sweeper_state: Mutex<SweeperState>,
+       broadcaster: B,
+       fee_estimator: E,
+       chain_data_source: Option<F>,
+       output_spender: O,
+       change_destination_source: D,
+       kv_store: K,
+       logger: L,
+}
+
+impl<B: Deref, D: Deref, E: Deref, F: Deref, K: Deref, L: Deref, O: Deref>
+       OutputSweeper<B, D, E, F, K, L, O>
+where
+       B::Target: BroadcasterInterface,
+       D::Target: ChangeDestinationSource,
+       E::Target: FeeEstimator,
+       F::Target: Filter + Sync + Send,
+       K::Target: KVStore,
+       L::Target: Logger,
+       O::Target: OutputSpender,
+{
+       /// Constructs a new [`OutputSweeper`].
+       ///
+       /// If chain data is provided via the [`Confirm`] interface or via filtered blocks, users also
+       /// need to register their [`Filter`] implementation via the given `chain_data_source`.
+       pub fn new(
+               best_block: BestBlock, broadcaster: B, fee_estimator: E, chain_data_source: Option<F>,
+               output_spender: O, change_destination_source: D, kv_store: K, logger: L,
+       ) -> Self {
+               let outputs = Vec::new();
+               let sweeper_state = Mutex::new(SweeperState { outputs, best_block });
+               Self {
+                       sweeper_state,
+                       broadcaster,
+                       fee_estimator,
+                       chain_data_source,
+                       output_spender,
+                       change_destination_source,
+                       kv_store,
+                       logger,
+               }
+       }
+
+       /// Tells the sweeper to track the given outputs descriptors.
+       ///
+       /// Usually, this should be called based on the values emitted by the
+       /// [`Event::SpendableOutputs`].
+       ///
+       /// The given `exclude_static_outputs` flag controls whether the sweeper will filter out
+       /// [`SpendableOutputDescriptor::StaticOutput`]s, which may be handled directly by the on-chain
+       /// wallet implementation.
+       ///
+       /// If `delay_until_height` is set, we will delay the spending until the respective block
+       /// height is reached. This can be used to batch spends, e.g., to reduce on-chain fees.
+       ///
+       /// Returns `Err` on persistence failure, in which case the call may be safely retried.
+       ///
+       /// [`Event::SpendableOutputs`]: crate::events::Event::SpendableOutputs
+       pub fn track_spendable_outputs(
+               &self, output_descriptors: Vec<SpendableOutputDescriptor>, channel_id: Option<ChannelId>,
+               exclude_static_outputs: bool, delay_until_height: Option<u32>,
+       ) -> Result<(), ()> {
+               let mut relevant_descriptors = output_descriptors
+                       .into_iter()
+                       .filter(|desc| {
+                               !(exclude_static_outputs
+                                       && matches!(desc, SpendableOutputDescriptor::StaticOutput { .. }))
+                       })
+                       .peekable();
+
+               if relevant_descriptors.peek().is_none() {
+                       return Ok(());
+               }
+
+               let spending_tx_opt;
+               {
+                       let mut state_lock = self.sweeper_state.lock().unwrap();
+                       for descriptor in relevant_descriptors {
+                               let output_info = TrackedSpendableOutput {
+                                       descriptor,
+                                       channel_id,
+                                       status: OutputSpendStatus::PendingInitialBroadcast {
+                                               delayed_until_height: delay_until_height,
+                                       },
+                               };
+
+                               if state_lock
+                                       .outputs
+                                       .iter()
+                                       .find(|o| o.descriptor == output_info.descriptor)
+                                       .is_some()
+                               {
+                                       continue;
+                               }
+
+                               state_lock.outputs.push(output_info);
+                       }
+                       spending_tx_opt = self.regenerate_spend_if_necessary(&mut *state_lock);
+                       self.persist_state(&*state_lock).map_err(|e| {
+                               log_error!(self.logger, "Error persisting OutputSweeper: {:?}", e);
+                       })?;
+               }
+
+               if let Some(spending_tx) = spending_tx_opt {
+                       self.broadcaster.broadcast_transactions(&[&spending_tx]);
+               }
+
+               Ok(())
+       }
+
+       /// Returns a list of the currently tracked spendable outputs.
+       pub fn tracked_spendable_outputs(&self) -> Vec<TrackedSpendableOutput> {
+               self.sweeper_state.lock().unwrap().outputs.clone()
+       }
+
+       /// Gets the latest best block which was connected either via the [`Listen`] or
+       /// [`Confirm`] interfaces.
+       pub fn current_best_block(&self) -> BestBlock {
+               self.sweeper_state.lock().unwrap().best_block
+       }
+
+       fn regenerate_spend_if_necessary(
+               &self, sweeper_state: &mut SweeperState,
+       ) -> Option<Transaction> {
+               let cur_height = sweeper_state.best_block.height;
+               let cur_hash = sweeper_state.best_block.block_hash;
+               let filter_fn = |o: &TrackedSpendableOutput| {
+                       if o.status.is_confirmed() {
+                               // Don't rebroadcast confirmed txs.
+                               return false;
+                       }
+
+                       if o.status.is_delayed(cur_height) {
+                               // Don't generate and broadcast if still delayed
+                               return false;
+                       }
+
+                       if o.status.latest_broadcast_height() >= Some(cur_height) {
+                               // Only broadcast once per block height.
+                               return false;
+                       }
+
+                       true
+               };
+
+               let respend_descriptors: Vec<&SpendableOutputDescriptor> =
+                       sweeper_state.outputs.iter().filter(|o| filter_fn(*o)).map(|o| &o.descriptor).collect();
+
+               if respend_descriptors.is_empty() {
+                       // Nothing to do.
+                       return None;
+               }
+
+               let spending_tx = match self.spend_outputs(&*sweeper_state, respend_descriptors) {
+                       Ok(spending_tx) => {
+                               log_debug!(
+                                       self.logger,
+                                       "Generating and broadcasting sweeping transaction {}",
+                                       spending_tx.txid()
+                               );
+                               spending_tx
+                       },
+                       Err(e) => {
+                               log_error!(self.logger, "Error spending outputs: {:?}", e);
+                               return None;
+                       },
+               };
+
+               // As we didn't modify the state so far, the same filter_fn yields the same elements as
+               // above.
+               let respend_outputs = sweeper_state.outputs.iter_mut().filter(|o| filter_fn(&**o));
+               for output_info in respend_outputs {
+                       if let Some(filter) = self.chain_data_source.as_ref() {
+                               let watched_output = output_info.to_watched_output(cur_hash);
+                               filter.register_output(watched_output);
+                       }
+
+                       output_info.status.broadcast(cur_hash, cur_height, spending_tx.clone());
+               }
+
+               Some(spending_tx)
+       }
+
+       fn prune_confirmed_outputs(&self, sweeper_state: &mut SweeperState) {
+               let cur_height = sweeper_state.best_block.height;
+
+               // Prune all outputs that have sufficient depth by now.
+               sweeper_state.outputs.retain(|o| {
+                       if let Some(confirmation_height) = o.status.confirmation_height() {
+                               if cur_height >= confirmation_height + ANTI_REORG_DELAY - 1 {
+                                       log_debug!(self.logger,
+                                               "Pruning swept output as sufficiently confirmed via spend in transaction {:?}. Pruned descriptor: {:?}",
+                                               o.status.latest_spending_tx().map(|t| t.txid()), o.descriptor
+                                       );
+                                       return false;
+                               }
+                       }
+                       true
+               });
+       }
+
+       fn persist_state(&self, sweeper_state: &SweeperState) -> Result<(), io::Error> {
+               self.kv_store
+                       .write(
+                               OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE,
+                               OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE,
+                               OUTPUT_SWEEPER_PERSISTENCE_KEY,
+                               &sweeper_state.encode(),
+                       )
+                       .map_err(|e| {
+                               log_error!(
+                                       self.logger,
+                                       "Write for key {}/{}/{} failed due to: {}",
+                                       OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE,
+                                       OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE,
+                                       OUTPUT_SWEEPER_PERSISTENCE_KEY,
+                                       e
+                               );
+                               e
+                       })
+       }
+
+       fn spend_outputs(
+               &self, sweeper_state: &SweeperState, descriptors: Vec<&SpendableOutputDescriptor>,
+       ) -> Result<Transaction, ()> {
+               let tx_feerate =
+                       self.fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::OutputSpendingFee);
+               let change_destination_script =
+                       self.change_destination_source.get_change_destination_script()?;
+               let cur_height = sweeper_state.best_block.height;
+               let locktime = Some(LockTime::from_height(cur_height).unwrap_or(LockTime::ZERO));
+               self.output_spender.spend_spendable_outputs(
+                       &descriptors,
+                       Vec::new(),
+                       change_destination_script,
+                       tx_feerate,
+                       locktime,
+                       &Secp256k1::new(),
+               )
+       }
+
+       fn transactions_confirmed_internal(
+               &self, sweeper_state: &mut SweeperState, header: &Header,
+               txdata: &chain::transaction::TransactionData, height: u32,
+       ) {
+               let confirmation_hash = header.block_hash();
+               for (_, tx) in txdata {
+                       for output_info in sweeper_state.outputs.iter_mut() {
+                               if output_info.is_spent_in(*tx) {
+                                       output_info.status.confirmed(confirmation_hash, height, (*tx).clone())
+                               }
+                       }
+               }
+       }
+
+       fn best_block_updated_internal(
+               &self, sweeper_state: &mut SweeperState, header: &Header, height: u32,
+       ) -> Option<Transaction> {
+               sweeper_state.best_block = BestBlock::new(header.block_hash(), height);
+               self.prune_confirmed_outputs(sweeper_state);
+               let spending_tx_opt = self.regenerate_spend_if_necessary(sweeper_state);
+               spending_tx_opt
+       }
+}
+
+impl<B: Deref, D: Deref, E: Deref, F: Deref, K: Deref, L: Deref, O: Deref> Listen
+       for OutputSweeper<B, D, E, F, K, L, O>
+where
+       B::Target: BroadcasterInterface,
+       D::Target: ChangeDestinationSource,
+       E::Target: FeeEstimator,
+       F::Target: Filter + Sync + Send,
+       K::Target: KVStore,
+       L::Target: Logger,
+       O::Target: OutputSpender,
+{
+       fn filtered_block_connected(
+               &self, header: &Header, txdata: &chain::transaction::TransactionData, height: u32,
+       ) {
+               let mut spending_tx_opt;
+               {
+                       let mut state_lock = self.sweeper_state.lock().unwrap();
+                       assert_eq!(state_lock.best_block.block_hash, header.prev_blockhash,
+                               "Blocks must be connected in chain-order - the connected header must build on the last connected header");
+                       assert_eq!(state_lock.best_block.height, height - 1,
+                               "Blocks must be connected in chain-order - the connected block height must be one greater than the previous height");
+
+                       self.transactions_confirmed_internal(&mut *state_lock, header, txdata, height);
+                       spending_tx_opt = self.best_block_updated_internal(&mut *state_lock, header, height);
+
+                       self.persist_state(&*state_lock).unwrap_or_else(|e| {
+                               log_error!(self.logger, "Error persisting OutputSweeper: {:?}", e);
+                               // Skip broadcasting if the persist failed.
+                               spending_tx_opt = None;
+                       });
+               }
+
+               if let Some(spending_tx) = spending_tx_opt {
+                       self.broadcaster.broadcast_transactions(&[&spending_tx]);
+               }
+       }
+
+       fn block_disconnected(&self, header: &Header, height: u32) {
+               let mut state_lock = self.sweeper_state.lock().unwrap();
+
+               let new_height = height - 1;
+               let block_hash = header.block_hash();
+
+               assert_eq!(state_lock.best_block.block_hash, block_hash,
+               "Blocks must be disconnected in chain-order - the disconnected header must be the last connected header");
+               assert_eq!(state_lock.best_block.height, height,
+                       "Blocks must be disconnected in chain-order - the disconnected block must have the correct height");
+               state_lock.best_block = BestBlock::new(header.prev_blockhash, new_height);
+
+               for output_info in state_lock.outputs.iter_mut() {
+                       if output_info.status.confirmation_hash() == Some(block_hash) {
+                               debug_assert_eq!(output_info.status.confirmation_height(), Some(height));
+                               output_info.status.unconfirmed();
+                       }
+               }
+
+               self.persist_state(&*state_lock).unwrap_or_else(|e| {
+                       log_error!(self.logger, "Error persisting OutputSweeper: {:?}", e);
+               });
+       }
+}
+
+impl<B: Deref, D: Deref, E: Deref, F: Deref, K: Deref, L: Deref, O: Deref> Confirm
+       for OutputSweeper<B, D, E, F, K, L, O>
+where
+       B::Target: BroadcasterInterface,
+       D::Target: ChangeDestinationSource,
+       E::Target: FeeEstimator,
+       F::Target: Filter + Sync + Send,
+       K::Target: KVStore,
+       L::Target: Logger,
+       O::Target: OutputSpender,
+{
+       fn transactions_confirmed(
+               &self, header: &Header, txdata: &chain::transaction::TransactionData, height: u32,
+       ) {
+               let mut state_lock = self.sweeper_state.lock().unwrap();
+               self.transactions_confirmed_internal(&mut *state_lock, header, txdata, height);
+               self.persist_state(&*state_lock).unwrap_or_else(|e| {
+                       log_error!(self.logger, "Error persisting OutputSweeper: {:?}", e);
+               });
+       }
+
+       fn transaction_unconfirmed(&self, txid: &Txid) {
+               let mut state_lock = self.sweeper_state.lock().unwrap();
+
+               // Get what height was unconfirmed.
+               let unconf_height = state_lock
+                       .outputs
+                       .iter()
+                       .find(|o| o.status.latest_spending_tx().map(|tx| tx.txid()) == Some(*txid))
+                       .and_then(|o| o.status.confirmation_height());
+
+               if let Some(unconf_height) = unconf_height {
+                       // Unconfirm all >= this height.
+                       state_lock
+                               .outputs
+                               .iter_mut()
+                               .filter(|o| o.status.confirmation_height() >= Some(unconf_height))
+                               .for_each(|o| o.status.unconfirmed());
+
+                       self.persist_state(&*state_lock).unwrap_or_else(|e| {
+                               log_error!(self.logger, "Error persisting OutputSweeper: {:?}", e);
+                       });
+               }
+       }
+
+       fn best_block_updated(&self, header: &Header, height: u32) {
+               let mut spending_tx_opt;
+               {
+                       let mut state_lock = self.sweeper_state.lock().unwrap();
+                       spending_tx_opt = self.best_block_updated_internal(&mut *state_lock, header, height);
+                       self.persist_state(&*state_lock).unwrap_or_else(|e| {
+                               log_error!(self.logger, "Error persisting OutputSweeper: {:?}", e);
+                               // Skip broadcasting if the persist failed.
+                               spending_tx_opt = None;
+                       });
+               }
+
+               if let Some(spending_tx) = spending_tx_opt {
+                       self.broadcaster.broadcast_transactions(&[&spending_tx]);
+               }
+       }
+
+       fn get_relevant_txids(&self) -> Vec<(Txid, u32, Option<BlockHash>)> {
+               let state_lock = self.sweeper_state.lock().unwrap();
+               state_lock
+                       .outputs
+                       .iter()
+                       .filter_map(|o| match o.status {
+                               OutputSpendStatus::PendingThresholdConfirmations {
+                                       ref latest_spending_tx,
+                                       confirmation_height,
+                                       confirmation_hash,
+                                       ..
+                               } => Some((latest_spending_tx.txid(), confirmation_height, Some(confirmation_hash))),
+                               _ => None,
+                       })
+                       .collect::<Vec<_>>()
+       }
+}
+
+#[derive(Debug, Clone)]
+struct SweeperState {
+       outputs: Vec<TrackedSpendableOutput>,
+       best_block: BestBlock,
+}
+
+impl_writeable_tlv_based!(SweeperState, {
+       (0, outputs, required_vec),
+       (2, best_block, required),
+});
+
+/// A `enum` signalling to the [`OutputSweeper`] that it should delay spending an output until a
+/// future block height is reached.
+#[derive(Debug, Clone)]
+pub enum SpendingDelay {
+       /// A relative delay indicating we shouldn't spend the output before `cur_height + num_blocks`
+       /// is reached.
+       Relative {
+               /// The number of blocks until we'll generate and broadcast the spending transaction.
+               num_blocks: u32,
+       },
+       /// An absolute delay indicating we shouldn't spend the output before `height` is reached.
+       Absolute {
+               /// The height at which we'll generate and broadcast the spending transaction.
+               height: u32,
+       },
+}
+
+impl<B: Deref, D: Deref, E: Deref, F: Deref, K: Deref, L: Deref, O: Deref>
+       ReadableArgs<(B, E, Option<F>, O, D, K, L)> for OutputSweeper<B, D, E, F, K, L, O>
+where
+       B::Target: BroadcasterInterface,
+       D::Target: ChangeDestinationSource,
+       E::Target: FeeEstimator,
+       F::Target: Filter + Sync + Send,
+       K::Target: KVStore,
+       L::Target: Logger,
+       O::Target: OutputSpender,
+{
+       #[inline]
+       fn read<R: io::Read>(
+               reader: &mut R, args: (B, E, Option<F>, O, D, K, L),
+       ) -> Result<Self, DecodeError> {
+               let (
+                       broadcaster,
+                       fee_estimator,
+                       chain_data_source,
+                       output_spender,
+                       change_destination_source,
+                       kv_store,
+                       logger,
+               ) = args;
+               let state = SweeperState::read(reader)?;
+               let best_block = state.best_block;
+
+               if let Some(filter) = chain_data_source.as_ref() {
+                       for output_info in &state.outputs {
+                               let watched_output = output_info.to_watched_output(best_block.block_hash);
+                               filter.register_output(watched_output);
+                       }
+               }
+
+               let sweeper_state = Mutex::new(state);
+               Ok(Self {
+                       sweeper_state,
+                       broadcaster,
+                       fee_estimator,
+                       chain_data_source,
+                       output_spender,
+                       change_destination_source,
+                       kv_store,
+                       logger,
+               })
+       }
+}
+
+impl<B: Deref, D: Deref, E: Deref, F: Deref, K: Deref, L: Deref, O: Deref>
+       ReadableArgs<(B, E, Option<F>, O, D, K, L)> for (BestBlock, OutputSweeper<B, D, E, F, K, L, O>)
+where
+       B::Target: BroadcasterInterface,
+       D::Target: ChangeDestinationSource,
+       E::Target: FeeEstimator,
+       F::Target: Filter + Sync + Send,
+       K::Target: KVStore,
+       L::Target: Logger,
+       O::Target: OutputSpender,
+{
+       #[inline]
+       fn read<R: io::Read>(
+               reader: &mut R, args: (B, E, Option<F>, O, D, K, L),
+       ) -> Result<Self, DecodeError> {
+               let (
+                       broadcaster,
+                       fee_estimator,
+                       chain_data_source,
+                       output_spender,
+                       change_destination_source,
+                       kv_store,
+                       logger,
+               ) = args;
+               let state = SweeperState::read(reader)?;
+               let best_block = state.best_block;
+
+               if let Some(filter) = chain_data_source.as_ref() {
+                       for output_info in &state.outputs {
+                               let watched_output = output_info.to_watched_output(best_block.block_hash);
+                               filter.register_output(watched_output);
+                       }
+               }
+
+               let sweeper_state = Mutex::new(state);
+               Ok((
+                       best_block,
+                       OutputSweeper {
+                               sweeper_state,
+                               broadcaster,
+                               fee_estimator,
+                               chain_data_source,
+                               output_spender,
+                               change_destination_source,
+                               kv_store,
+                               logger,
+                       },
+               ))
+       }
+}
index bfa3e32c91f5b3d683c26d5b9f9bf2700512efb6..2009e4d0b08a9a22fd68941b8290bfabffe75203 100644 (file)
 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::channel_keys::{HtlcKey};
-use crate::ln::{msgs, PaymentPreimage};
+use crate::ln::msgs;
+use crate::ln::types::PaymentPreimage;
 use crate::sign::{InMemorySigner, ChannelSigner};
-use crate::sign::ecdsa::{EcdsaChannelSigner, WriteableEcdsaChannelSigner};
+use crate::sign::ecdsa::EcdsaChannelSigner;
 
+#[allow(unused_imports)]
 use crate::prelude::*;
+
 use core::cmp;
 use crate::sync::{Mutex, Arc};
 #[cfg(test)] use crate::sync::MutexGuard;
@@ -247,8 +250,8 @@ impl EcdsaChannelSigner for TestChannelSigner {
                        } else {
                                EcdsaSighashType::All
                        };
-                       let sighash = &sighash::SighashCache::new(&*htlc_tx).segwit_signature_hash(
-                               input, &witness_script, htlc_descriptor.htlc.amount_msat / 1000, sighash_type
+                       let sighash = &sighash::SighashCache::new(&*htlc_tx).p2wsh_signature_hash(
+                               input, &witness_script, htlc_descriptor.htlc.to_bitcoin_amount(), sighash_type
                        ).unwrap();
                        let countersignatory_htlc_key = HtlcKey::from_basepoint(
                                &secp_ctx, &self.inner.counterparty_pubkeys().unwrap().htlc_basepoint, &htlc_descriptor.per_commitment_point,
@@ -294,8 +297,6 @@ impl EcdsaChannelSigner for TestChannelSigner {
        }
 }
 
-impl WriteableEcdsaChannelSigner for TestChannelSigner {}
-
 #[cfg(taproot)]
 impl TaprootChannelSigner for TestChannelSigner {
        fn generate_local_nonce_pair(&self, commitment_number: u64, secp_ctx: &Secp256k1<All>) -> PublicNonce {
index 6ad2e76df870e0e8dc7b28e0636f6381d0ce0710..74bcd607e16e58fc1d28ce222932a09c8ec85618 100644 (file)
@@ -8,14 +8,15 @@
 // licenses.
 
 use crate::blinded_path::BlindedPath;
+use crate::blinded_path::message::ForwardNode;
 use crate::blinded_path::payment::ReceiveTlvs;
 use crate::chain;
 use crate::chain::WatchedOutput;
 use crate::chain::chaininterface;
 use crate::chain::chaininterface::ConfirmationTarget;
+#[cfg(test)]
 use crate::chain::chaininterface::FEERATE_FLOOR_SATS_PER_KW;
 use crate::chain::chainmonitor;
-use crate::chain::chainmonitor::{MonitorUpdateId, UpdateOrigin};
 use crate::chain::channelmonitor;
 use crate::chain::channelmonitor::MonitorEvent;
 use crate::chain::transaction::OutPoint;
@@ -23,8 +24,9 @@ use crate::routing::router::{CandidateRouteHop, FirstHopCandidate, PublicHopCand
 use crate::sign;
 use crate::events;
 use crate::events::bump_transaction::{WalletSource, Utxo};
-use crate::ln::ChannelId;
+use crate::ln::types::ChannelId;
 use crate::ln::channelmanager::{ChannelDetails, self};
+#[cfg(test)]
 use crate::ln::chan_utils::CommitmentTransaction;
 use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
 use crate::ln::{msgs, wire};
@@ -44,14 +46,16 @@ use crate::util::logger::{Logger, Level, Record};
 use crate::util::ser::{Readable, ReadableArgs, Writer, Writeable};
 use crate::util::persist::KVStore;
 
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::constants::ChainHash;
 use bitcoin::blockdata::constants::genesis_block;
 use bitcoin::blockdata::transaction::{Transaction, TxOut};
 use bitcoin::blockdata::script::{Builder, Script, ScriptBuf};
 use bitcoin::blockdata::opcodes;
 use bitcoin::blockdata::block::Block;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
 use bitcoin::hash_types::{BlockHash, Txid};
+use bitcoin::hashes::Hash;
 use bitcoin::sighash::{SighashCache, EcdsaSighashType};
 
 use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, self};
@@ -59,9 +63,6 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
 use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
 use bitcoin::secp256k1::schnorr;
 
-#[cfg(any(test, feature = "_test_utils"))]
-use regex;
-
 use crate::io;
 use crate::prelude::*;
 use core::cell::RefCell;
@@ -69,12 +70,12 @@ use core::time::Duration;
 use crate::sync::{Mutex, Arc};
 use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
 use core::mem;
-use bitcoin::bech32::u5;
+use bech32::u5;
 use crate::sign::{InMemorySigner, RandomBytes, Recipient, EntropySource, NodeSigner, SignerProvider};
 
 #[cfg(feature = "std")]
 use std::time::{SystemTime, UNIX_EPOCH};
-use bitcoin::psbt::PartiallySignedTransaction;
+use bitcoin::psbt::Psbt;
 use bitcoin::Sequence;
 
 pub fn pubkey(byte: u8) -> PublicKey {
@@ -114,7 +115,7 @@ pub struct TestRouter<'a> {
        >,
        //pub entropy_source: &'a RandomBytes,
        pub network_graph: Arc<NetworkGraph<&'a TestLogger>>,
-       pub next_routes: Mutex<VecDeque<(RouteParameters, Result<Route, LightningError>)>>,
+       pub next_routes: Mutex<VecDeque<(RouteParameters, Option<Result<Route, LightningError>>)>>,
        pub scorer: &'a RwLock<TestScorer>,
 }
 
@@ -134,7 +135,12 @@ impl<'a> TestRouter<'a> {
 
        pub fn expect_find_route(&self, query: RouteParameters, result: Result<Route, LightningError>) {
                let mut expected_routes = self.next_routes.lock().unwrap();
-               expected_routes.push_back((query, result));
+               expected_routes.push_back((query, Some(result)));
+       }
+
+       pub fn expect_find_route_query(&self, query: RouteParameters) {
+               let mut expected_routes = self.next_routes.lock().unwrap();
+               expected_routes.push_back((query, None));
        }
 }
 
@@ -147,67 +153,71 @@ impl<'a> Router for TestRouter<'a> {
                let next_route_opt = self.next_routes.lock().unwrap().pop_front();
                if let Some((find_route_query, find_route_res)) = next_route_opt {
                        assert_eq!(find_route_query, *params);
-                       if let Ok(ref route) = find_route_res {
-                               assert_eq!(route.route_params, Some(find_route_query));
-                               let scorer = self.scorer.read().unwrap();
-                               let scorer = ScorerAccountingForInFlightHtlcs::new(scorer, &inflight_htlcs);
-                               for path in &route.paths {
-                                       let mut aggregate_msat = 0u64;
-                                       let mut prev_hop_node = payer;
-                                       for (idx, hop) in path.hops.iter().rev().enumerate() {
-                                               aggregate_msat += hop.fee_msat;
-                                               let usage = ChannelUsage {
-                                                       amount_msat: aggregate_msat,
-                                                       inflight_htlc_msat: 0,
-                                                       effective_capacity: EffectiveCapacity::Unknown,
-                                               };
-
-                                               if idx == path.hops.len() - 1 {
-                                                       if let Some(first_hops) = first_hops {
-                                                               if let Some(idx) = first_hops.iter().position(|h| h.get_outbound_payment_scid() == Some(hop.short_channel_id)) {
-                                                                       let node_id = NodeId::from_pubkey(payer);
-                                                                       let candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {
-                                                                               details: first_hops[idx],
-                                                                               payer_node_id: &node_id,
-                                                                               payer_node_counter: u32::max_value(),
-                                                                               target_node_counter: u32::max_value(),
-                                                                       });
-                                                                       scorer.channel_penalty_msat(&candidate, usage, &Default::default());
-                                                                       continue;
+                       if let Some(res) = find_route_res {
+                               if let Ok(ref route) = res {
+                                       assert_eq!(route.route_params, Some(find_route_query));
+                                       let scorer = self.scorer.read().unwrap();
+                                       let scorer = ScorerAccountingForInFlightHtlcs::new(scorer, &inflight_htlcs);
+                                       for path in &route.paths {
+                                               let mut aggregate_msat = 0u64;
+                                               let mut prev_hop_node = payer;
+                                               for (idx, hop) in path.hops.iter().rev().enumerate() {
+                                                       aggregate_msat += hop.fee_msat;
+                                                       let usage = ChannelUsage {
+                                                               amount_msat: aggregate_msat,
+                                                               inflight_htlc_msat: 0,
+                                                               effective_capacity: EffectiveCapacity::Unknown,
+                                                       };
+
+                                                       if idx == path.hops.len() - 1 {
+                                                               if let Some(first_hops) = first_hops {
+                                                                       if let Some(idx) = first_hops.iter().position(|h| h.get_outbound_payment_scid() == Some(hop.short_channel_id)) {
+                                                                               let node_id = NodeId::from_pubkey(payer);
+                                                                               let candidate = CandidateRouteHop::FirstHop(FirstHopCandidate {
+                                                                                       details: first_hops[idx],
+                                                                                       payer_node_id: &node_id,
+                                                                                       payer_node_counter: u32::max_value(),
+                                                                                       target_node_counter: u32::max_value(),
+                                                                               });
+                                                                               scorer.channel_penalty_msat(&candidate, usage, &Default::default());
+                                                                               continue;
+                                                                       }
                                                                }
                                                        }
+                                                       let network_graph = self.network_graph.read_only();
+                                                       if let Some(channel) = network_graph.channel(hop.short_channel_id) {
+                                                               let (directed, _) = channel.as_directed_to(&NodeId::from_pubkey(&hop.pubkey)).unwrap();
+                                                               let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
+                                                                       info: directed,
+                                                                       short_channel_id: hop.short_channel_id,
+                                                               });
+                                                               scorer.channel_penalty_msat(&candidate, usage, &Default::default());
+                                                       } else {
+                                                               let target_node_id = NodeId::from_pubkey(&hop.pubkey);
+                                                               let route_hint = RouteHintHop {
+                                                                       src_node_id: *prev_hop_node,
+                                                                       short_channel_id: hop.short_channel_id,
+                                                                       fees: RoutingFees { base_msat: 0, proportional_millionths: 0 },
+                                                                       cltv_expiry_delta: 0,
+                                                                       htlc_minimum_msat: None,
+                                                                       htlc_maximum_msat: None,
+                                                               };
+                                                               let candidate = CandidateRouteHop::PrivateHop(PrivateHopCandidate {
+                                                                       hint: &route_hint,
+                                                                       target_node_id: &target_node_id,
+                                                                       source_node_counter: u32::max_value(),
+                                                                       target_node_counter: u32::max_value(),
+                                                               });
+                                                               scorer.channel_penalty_msat(&candidate, usage, &Default::default());
+                                                       }
+                                                       prev_hop_node = &hop.pubkey;
                                                }
-                                               let network_graph = self.network_graph.read_only();
-                                               if let Some(channel) = network_graph.channel(hop.short_channel_id) {
-                                                       let (directed, _) = channel.as_directed_to(&NodeId::from_pubkey(&hop.pubkey)).unwrap();
-                                                       let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
-                                                               info: directed,
-                                                               short_channel_id: hop.short_channel_id,
-                                                       });
-                                                       scorer.channel_penalty_msat(&candidate, usage, &Default::default());
-                                               } else {
-                                                       let target_node_id = NodeId::from_pubkey(&hop.pubkey);
-                                                       let route_hint = RouteHintHop {
-                                                               src_node_id: *prev_hop_node,
-                                                               short_channel_id: hop.short_channel_id,
-                                                               fees: RoutingFees { base_msat: 0, proportional_millionths: 0 },
-                                                               cltv_expiry_delta: 0,
-                                                               htlc_minimum_msat: None,
-                                                               htlc_maximum_msat: None,
-                                                       };
-                                                       let candidate = CandidateRouteHop::PrivateHop(PrivateHopCandidate {
-                                                               hint: &route_hint,
-                                                               target_node_id: &target_node_id,
-                                                               source_node_counter: u32::max_value(),
-                                                               target_node_counter: u32::max_value(),
-                                                       });
-                                                       scorer.channel_penalty_msat(&candidate, usage, &Default::default());
-                                               }
-                                               prev_hop_node = &hop.pubkey;
                                        }
                                }
+                               route_res = res;
+                       } else {
+                               route_res = self.router.find_route(payer, params, first_hops, inflight_htlcs);
                        }
-                       route_res = find_route_res;
                } else {
                        route_res = self.router.find_route(payer, params, first_hops, inflight_htlcs);
                };
@@ -243,7 +253,7 @@ impl<'a> MessageRouter for TestRouter<'a> {
        fn create_blinded_paths<
                T: secp256k1::Signing + secp256k1::Verification
        >(
-               &self, recipient: PublicKey, peers: Vec<PublicKey>, secp_ctx: &Secp256k1<T>,
+               &self, recipient: PublicKey, peers: Vec<ForwardNode>, secp_ctx: &Secp256k1<T>,
        ) -> Result<Vec<BlindedPath>, ()> {
                self.router.create_blinded_paths(recipient, peers, secp_ctx)
        }
@@ -278,7 +288,7 @@ impl<'a> MessageRouter for TestMessageRouter<'a> {
        }
 
        fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
-               &self, recipient: PublicKey, peers: Vec<PublicKey>, secp_ctx: &Secp256k1<T>,
+               &self, recipient: PublicKey, peers: Vec<ForwardNode>, secp_ctx: &Secp256k1<T>,
        ) -> Result<Vec<BlindedPath>, ()> {
                self.inner.create_blinded_paths(recipient, peers, secp_ctx)
        }
@@ -316,7 +326,7 @@ impl SignerProvider for OnlyReadsKeysInterface {
 pub struct TestChainMonitor<'a> {
        pub added_monitors: Mutex<Vec<(OutPoint, channelmonitor::ChannelMonitor<TestChannelSigner>)>>,
        pub monitor_updates: Mutex<HashMap<ChannelId, Vec<channelmonitor::ChannelMonitorUpdate>>>,
-       pub latest_monitor_update_id: Mutex<HashMap<ChannelId, (OutPoint, u64, MonitorUpdateId)>>,
+       pub latest_monitor_update_id: Mutex<HashMap<ChannelId, (OutPoint, u64, u64)>>,
        pub chain_monitor: chainmonitor::ChainMonitor<TestChannelSigner, &'a TestChainSource, &'a dyn chaininterface::BroadcasterInterface, &'a TestFeeEstimator, &'a TestLogger, &'a dyn chainmonitor::Persist<TestChannelSigner>>,
        pub keys_manager: &'a TestKeysInterface,
        /// If this is set to Some(), the next update_channel call (not watch_channel) must be a
@@ -355,7 +365,7 @@ impl<'a> chain::Watch<TestChannelSigner> for TestChainMonitor<'a> {
                        &mut io::Cursor::new(&w.0), (self.keys_manager, self.keys_manager)).unwrap().1;
                assert!(new_monitor == monitor);
                self.latest_monitor_update_id.lock().unwrap().insert(monitor.channel_id(),
-                       (funding_txo, monitor.get_latest_update_id(), MonitorUpdateId::from_new_monitor(&monitor)));
+                       (funding_txo, monitor.get_latest_update_id(), monitor.get_latest_update_id()));
                self.added_monitors.lock().unwrap().push((funding_txo, monitor));
                self.chain_monitor.watch_channel(funding_txo, new_monitor)
        }
@@ -379,7 +389,7 @@ impl<'a> chain::Watch<TestChannelSigner> for TestChainMonitor<'a> {
                }
 
                self.latest_monitor_update_id.lock().unwrap().insert(channel_id,
-                       (funding_txo, update.update_id, MonitorUpdateId::from_monitor_update(update)));
+                       (funding_txo, update.update_id, update.update_id));
                let update_res = self.chain_monitor.update_channel(funding_txo, update);
                // At every point where we get a monitor update, we should be able to send a useful monitor
                // to a watchtower and disk...
@@ -403,12 +413,14 @@ impl<'a> chain::Watch<TestChannelSigner> for TestChainMonitor<'a> {
        }
 }
 
+#[cfg(test)]
 struct JusticeTxData {
        justice_tx: Transaction,
-       value: u64,
+       value: Amount,
        commitment_number: u64,
 }
 
+#[cfg(test)]
 pub(crate) struct WatchtowerPersister {
        persister: TestPersister,
        /// Upon a new commitment_signed, we'll get a
@@ -422,6 +434,7 @@ pub(crate) struct WatchtowerPersister {
        destination_script: ScriptBuf,
 }
 
+#[cfg(test)]
 impl WatchtowerPersister {
        #[cfg(test)]
        pub(crate) fn new(destination_script: ScriptBuf) -> Self {
@@ -452,11 +465,12 @@ impl WatchtowerPersister {
        }
 }
 
-impl<Signer: sign::ecdsa::WriteableEcdsaChannelSigner> chainmonitor::Persist<Signer> for WatchtowerPersister {
+#[cfg(test)]
+impl<Signer: sign::ecdsa::EcdsaChannelSigner> chainmonitor::Persist<Signer> for WatchtowerPersister {
        fn persist_new_channel(&self, funding_txo: OutPoint,
-               data: &channelmonitor::ChannelMonitor<Signer>, id: MonitorUpdateId
+               data: &channelmonitor::ChannelMonitor<Signer>
        ) -> chain::ChannelMonitorUpdateStatus {
-               let res = self.persister.persist_new_channel(funding_txo, data, id);
+               let res = self.persister.persist_new_channel(funding_txo, data);
 
                assert!(self.unsigned_justice_tx_data.lock().unwrap()
                        .insert(funding_txo, VecDeque::new()).is_none());
@@ -476,9 +490,9 @@ impl<Signer: sign::ecdsa::WriteableEcdsaChannelSigner> chainmonitor::Persist<Sig
 
        fn update_persisted_channel(
                &self, funding_txo: OutPoint, update: Option<&channelmonitor::ChannelMonitorUpdate>,
-               data: &channelmonitor::ChannelMonitor<Signer>, update_id: MonitorUpdateId
+               data: &channelmonitor::ChannelMonitor<Signer>
        ) -> chain::ChannelMonitorUpdateStatus {
-               let res = self.persister.update_persisted_channel(funding_txo, update, data, update_id);
+               let res = self.persister.update_persisted_channel(funding_txo, update, data);
 
                if let Some(update) = update {
                        let commitment_txs = data.counterparty_commitment_txs_from_update(update);
@@ -491,7 +505,7 @@ impl<Signer: sign::ecdsa::WriteableEcdsaChannelSigner> chainmonitor::Persist<Sig
                        while let Some(JusticeTxData { justice_tx, value, commitment_number }) = channel_state.front() {
                                let input_idx = 0;
                                let commitment_txid = justice_tx.input[input_idx].previous_output.txid;
-                               match data.sign_to_local_justice_tx(justice_tx.clone(), input_idx, *value, *commitment_number) {
+                               match data.sign_to_local_justice_tx(justice_tx.clone(), input_idx, value.to_sat(), *commitment_number) {
                                        Ok(signed_justice_tx) => {
                                                let dup = self.watchtower_state.lock().unwrap()
                                                        .get_mut(&funding_txo).unwrap()
@@ -505,24 +519,26 @@ impl<Signer: sign::ecdsa::WriteableEcdsaChannelSigner> chainmonitor::Persist<Sig
                }
                res
        }
+
+       fn archive_persisted_channel(&self, funding_txo: OutPoint) {
+               <TestPersister as chainmonitor::Persist<TestChannelSigner>>::archive_persisted_channel(&self.persister, funding_txo);
+       }
 }
 
 pub struct TestPersister {
        /// The queue of update statuses we'll return. If none are queued, ::Completed will always be
        /// returned.
        pub update_rets: Mutex<VecDeque<chain::ChannelMonitorUpdateStatus>>,
-       /// When we get an update_persisted_channel call with no ChannelMonitorUpdate, we insert the
-       /// MonitorUpdateId here.
-       pub chain_sync_monitor_persistences: Mutex<HashMap<OutPoint, HashSet<MonitorUpdateId>>>,
        /// When we get an update_persisted_channel call *with* a ChannelMonitorUpdate, we insert the
-       /// MonitorUpdateId here.
-       pub offchain_monitor_updates: Mutex<HashMap<OutPoint, HashSet<MonitorUpdateId>>>,
+       /// [`ChannelMonitor::get_latest_update_id`] here.
+       ///
+       /// [`ChannelMonitor`]: channelmonitor::ChannelMonitor
+       pub offchain_monitor_updates: Mutex<HashMap<OutPoint, HashSet<u64>>>,
 }
 impl TestPersister {
        pub fn new() -> Self {
                Self {
                        update_rets: Mutex::new(VecDeque::new()),
-                       chain_sync_monitor_persistences: Mutex::new(new_hash_map()),
                        offchain_monitor_updates: Mutex::new(new_hash_map()),
                }
        }
@@ -532,27 +548,30 @@ impl TestPersister {
                self.update_rets.lock().unwrap().push_back(next_ret);
        }
 }
-impl<Signer: sign::ecdsa::WriteableEcdsaChannelSigner> chainmonitor::Persist<Signer> for TestPersister {
-       fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor<Signer>, _id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus {
+impl<Signer: sign::ecdsa::EcdsaChannelSigner> chainmonitor::Persist<Signer> for TestPersister {
+       fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor<Signer>) -> chain::ChannelMonitorUpdateStatus {
                if let Some(update_ret) = self.update_rets.lock().unwrap().pop_front() {
                        return update_ret
                }
                chain::ChannelMonitorUpdateStatus::Completed
        }
 
-       fn update_persisted_channel(&self, funding_txo: OutPoint, _update: Option<&channelmonitor::ChannelMonitorUpdate>, _data: &channelmonitor::ChannelMonitor<Signer>, update_id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus {
+       fn update_persisted_channel(&self, funding_txo: OutPoint, update: Option<&channelmonitor::ChannelMonitorUpdate>, _data: &channelmonitor::ChannelMonitor<Signer>) -> chain::ChannelMonitorUpdateStatus {
                let mut ret = chain::ChannelMonitorUpdateStatus::Completed;
                if let Some(update_ret) = self.update_rets.lock().unwrap().pop_front() {
                        ret = update_ret;
                }
-               let is_chain_sync = if let UpdateOrigin::ChainSync(_) = update_id.contents { true } else { false };
-               if is_chain_sync {
-                       self.chain_sync_monitor_persistences.lock().unwrap().entry(funding_txo).or_insert(new_hash_set()).insert(update_id);
-               } else {
-                       self.offchain_monitor_updates.lock().unwrap().entry(funding_txo).or_insert(new_hash_set()).insert(update_id);
+
+               if let Some(update) = update  {
+                       self.offchain_monitor_updates.lock().unwrap().entry(funding_txo).or_insert(new_hash_set()).insert(update.update_id);
                }
                ret
        }
+
+       fn archive_persisted_channel(&self, funding_txo: OutPoint) {
+               // remove the channel from the offchain_monitor_updates map
+               self.offchain_monitor_updates.lock().unwrap().remove(&funding_txo);
+       }
 }
 
 pub struct TestStore {
@@ -769,12 +788,15 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler {
        fn handle_stfu(&self, _their_node_id: &PublicKey, msg: &msgs::Stfu) {
                self.received_msg(wire::Message::Stfu(msg.clone()));
        }
+       #[cfg(splicing)]
        fn handle_splice(&self, _their_node_id: &PublicKey, msg: &msgs::Splice) {
                self.received_msg(wire::Message::Splice(msg.clone()));
        }
+       #[cfg(splicing)]
        fn handle_splice_ack(&self, _their_node_id: &PublicKey, msg: &msgs::SpliceAck) {
                self.received_msg(wire::Message::SpliceAck(msg.clone()));
        }
+       #[cfg(splicing)]
        fn handle_splice_locked(&self, _their_node_id: &PublicKey, msg: &msgs::SpliceLocked) {
                self.received_msg(wire::Message::SpliceLocked(msg.clone()));
        }
@@ -1154,7 +1176,7 @@ impl NodeSigner for TestNodeSigner {
                Ok(SharedSecret::new(other_key, &node_secret))
        }
 
-       fn sign_invoice(&self, _: &[u8], _: &[bitcoin::bech32::u5], _: Recipient) -> Result<bitcoin::secp256k1::ecdsa::RecoverableSignature, ()> {
+       fn sign_invoice(&self, _: &[u8], _: &[bech32::u5], _: Recipient) -> Result<bitcoin::secp256k1::ecdsa::RecoverableSignature, ()> {
                unreachable!()
        }
 
@@ -1358,12 +1380,16 @@ impl TestChainSource {
                let script_pubkey = Builder::new().push_opcode(opcodes::OP_TRUE).into_script();
                Self {
                        chain_hash: ChainHash::using_genesis_block(network),
-                       utxo_ret: Mutex::new(UtxoResult::Sync(Ok(TxOut { value: u64::max_value(), script_pubkey }))),
+                       utxo_ret: Mutex::new(UtxoResult::Sync(Ok(TxOut { value: Amount::MAX, script_pubkey }))),
                        get_utxo_call_count: AtomicUsize::new(0),
                        watched_txn: Mutex::new(new_hash_set()),
                        watched_outputs: Mutex::new(new_hash_set()),
                }
        }
+       pub fn remove_watched_txn_and_outputs(&self, outpoint: OutPoint, script_pubkey: ScriptBuf) {
+               self.watched_outputs.lock().unwrap().remove(&(outpoint, script_pubkey.clone()));
+               self.watched_txn.lock().unwrap().remove(&(outpoint.txid, script_pubkey));
+       }
 }
 
 impl UtxoLookup for TestChainSource {
@@ -1485,7 +1511,7 @@ impl TestWalletSource {
                }
        }
 
-       pub fn add_utxo(&self, outpoint: bitcoin::OutPoint, value: u64) -> TxOut {
+       pub fn add_utxo(&self, outpoint: bitcoin::OutPoint, value: Amount) -> TxOut {
                let public_key = bitcoin::PublicKey::new(self.secret_key.public_key(&self.secp));
                let utxo = Utxo::new_p2pkh(outpoint, value, &public_key.pubkey_hash());
                self.utxos.borrow_mut().push(utxo.clone());
@@ -1513,15 +1539,15 @@ impl WalletSource for TestWalletSource {
                Ok(ScriptBuf::new_p2pkh(&public_key.pubkey_hash()))
        }
 
-       fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()> {
-               let mut tx = psbt.extract_tx();
+       fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
+               let mut tx = psbt.extract_tx_unchecked_fee_rate();
                let utxos = self.utxos.borrow();
                for i in 0..tx.input.len() {
                        if let Some(utxo) = utxos.iter().find(|utxo| utxo.outpoint == tx.input[i].previous_output) {
                                let sighash = SighashCache::new(&tx)
                                        .legacy_signature_hash(i, &utxo.output.script_pubkey, EcdsaSighashType::All as u32)
                                        .map_err(|_| ())?;
-                               let sig = self.secp.sign_ecdsa(&(*sighash.as_raw_hash()).into(), &self.secret_key);
+                               let sig = self.secp.sign_ecdsa(&secp256k1::Message::from_digest(sighash.to_byte_array()), &self.secret_key);
                                let bitcoin_sig = bitcoin::ecdsa::Signature { sig, hash_ty: EcdsaSighashType::All };
                                tx.input[i].script_sig = Builder::new()
                                        .push_slice(&bitcoin_sig.serialize())
index a6e6f4d1fda6b6e58a102a0baeb97cbed102159f..bedeab1d4890ac78c0dbda3f2506e8a28ee9f07d 100644 (file)
@@ -16,16 +16,8 @@ pub trait Time: Copy + Sub<Duration, Output = Self> where Self: Sized {
        /// Returns an instance corresponding to the current moment.
        fn now() -> Self;
 
-       /// Returns the amount of time elapsed since `self` was created.
-       fn elapsed(&self) -> Duration;
-
        /// Returns the amount of time passed between `earlier` and `self`.
        fn duration_since(&self, earlier: Self) -> Duration;
-
-       /// Returns the amount of time passed since the beginning of [`Time`].
-       ///
-       /// Used during (de-)serialization.
-       fn duration_since_epoch() -> Duration;
 }
 
 /// A state in which time has no meaning.
@@ -40,14 +32,6 @@ impl Time for Eternity {
        fn duration_since(&self, _earlier: Self) -> Duration {
                Duration::from_secs(0)
        }
-
-       fn duration_since_epoch() -> Duration {
-               Duration::from_secs(0)
-       }
-
-       fn elapsed(&self) -> Duration {
-               Duration::from_secs(0)
-       }
 }
 
 impl Sub<Duration> for Eternity {
@@ -82,15 +66,6 @@ impl Time for MonotonicTime {
                let now = Self::now();
                if now.0 > earlier.0 { now.0 - earlier.0 } else { Duration::from_secs(0) }
        }
-
-       fn duration_since_epoch() -> Duration {
-               use std::time::SystemTime;
-               SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap()
-       }
-
-       fn elapsed(&self) -> Duration {
-               Self::now().0 - self.0
-       }
 }
 
 #[cfg(feature = "std")]
@@ -127,20 +102,12 @@ pub mod tests {
 
        impl Time for SinceEpoch {
                fn now() -> Self {
-                       Self(Self::duration_since_epoch())
+                       Self(Self::ELAPSED.with(|elapsed| elapsed.get()))
                }
 
                fn duration_since(&self, earlier: Self) -> Duration {
                        self.0 - earlier.0
                }
-
-               fn duration_since_epoch() -> Duration {
-                       Self::ELAPSED.with(|elapsed| elapsed.get())
-               }
-
-               fn elapsed(&self) -> Duration {
-                       Self::duration_since_epoch() - self.0
-               }
        }
 
        impl Sub<Duration> for SinceEpoch {
@@ -154,36 +121,20 @@ pub mod tests {
        #[test]
        fn time_passes_when_advanced() {
                let now = SinceEpoch::now();
-               assert_eq!(now.elapsed(), Duration::from_secs(0));
 
                SinceEpoch::advance(Duration::from_secs(1));
                SinceEpoch::advance(Duration::from_secs(1));
 
-               let elapsed = now.elapsed();
                let later = SinceEpoch::now();
 
-               assert_eq!(elapsed, Duration::from_secs(2));
-               assert_eq!(later - elapsed, now);
+               assert_eq!(now.0 + Duration::from_secs(2), later.0);
        }
 
        #[test]
        fn time_never_passes_in_an_eternity() {
                let now = Eternity::now();
-               let elapsed = now.elapsed();
                let later = Eternity::now();
 
-               assert_eq!(now.elapsed(), Duration::from_secs(0));
-               assert_eq!(later - elapsed, now);
-       }
-
-       #[test]
-       #[cfg(feature = "std")]
-       fn monotonic_time_subtracts() {
-               let now = super::MonotonicTime::now();
-               assert!(now.elapsed() < Duration::from_secs(10));
-
-               let ten_years = Duration::from_secs(10 * 365 * 24 * 60 * 60);
-               let past = now - ten_years;
-               assert!(past.elapsed() >= ten_years);
+               assert_eq!(later, now);
        }
 }
index 12b504a69ef8dd50aceeeaed1ea9431fd905d8eb..5eb7ba3b94966a16ef9d927f6dcb6d117035b936 100644 (file)
@@ -7,14 +7,15 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
+use bitcoin::amount::Amount;
 use bitcoin::blockdata::transaction::{Transaction, TxOut};
 use bitcoin::blockdata::script::ScriptBuf;
 use bitcoin::consensus::Encodable;
 use bitcoin::consensus::encode::VarInt;
 
-use crate::ln::msgs::MAX_VALUE_MSAT;
-
+#[allow(unused_imports)]
 use crate::prelude::*;
+
 use crate::io_extras::sink;
 use core::cmp::Ordering;
 
@@ -33,12 +34,12 @@ pub fn sort_outputs<T, C : Fn(&T, &T) -> Ordering>(outputs: &mut Vec<(TxOut, T)>
 /// Assumes at least one input will have a witness (ie spends a segwit output).
 /// Returns an Err(()) if the requested feerate cannot be met.
 /// Returns the expected maximum weight of the fully signed transaction on success.
-pub(crate) fn maybe_add_change_output(tx: &mut Transaction, input_value: u64, witness_max_weight: u64, feerate_sat_per_1000_weight: u32, change_destination_script: ScriptBuf) -> Result<u64, ()> {
-       if input_value > MAX_VALUE_MSAT / 1000 { return Err(()); }
+pub(crate) fn maybe_add_change_output(tx: &mut Transaction, input_value: Amount, witness_max_weight: u64, feerate_sat_per_1000_weight: u32, change_destination_script: ScriptBuf) -> Result<u64, ()> {
+       if input_value > Amount::MAX_MONEY { return Err(()); }
 
        const WITNESS_FLAG_BYTES: u64 = 2;
 
-       let mut output_value = 0;
+       let mut output_value = Amount::ZERO;
        for output in tx.output.iter() {
                output_value += output.value;
                if output_value >= input_value { return Err(()); }
@@ -47,20 +48,20 @@ pub(crate) fn maybe_add_change_output(tx: &mut Transaction, input_value: u64, wi
        let dust_value = change_destination_script.dust_value();
        let mut change_output = TxOut {
                script_pubkey: change_destination_script,
-               value: 0,
+               value: Amount::ZERO,
        };
        let change_len = change_output.consensus_encode(&mut sink()).unwrap();
        let starting_weight = tx.weight().to_wu() + WITNESS_FLAG_BYTES + witness_max_weight as u64;
        let mut weight_with_change: i64 = starting_weight as i64 + change_len as i64 * 4;
        // Include any extra bytes required to push an extra output.
-       weight_with_change += (VarInt(tx.output.len() as u64 + 1).len() - VarInt(tx.output.len() as u64).len()) as i64 * 4;
+       weight_with_change += (VarInt(tx.output.len() as u64 + 1).size() - VarInt(tx.output.len() as u64).size()) as i64 * 4;
        // When calculating weight, add two for the flag bytes
-       let change_value: i64 = (input_value - output_value) as i64 - weight_with_change * feerate_sat_per_1000_weight as i64 / 1000;
+       let change_value: i64 = (input_value - output_value).to_sat() as i64 - weight_with_change * feerate_sat_per_1000_weight as i64 / 1000;
        if change_value >= dust_value.to_sat() as i64 {
-               change_output.value = change_value as u64;
+               change_output.value = Amount::from_sat(change_value as u64);
                tx.output.push(change_output);
                Ok(weight_with_change as u64)
-       } else if (input_value - output_value) as i64 - (starting_weight as i64) * feerate_sat_per_1000_weight as i64 / 1000 < 0 {
+       } else if (input_value - output_value).to_sat() as i64 - (starting_weight as i64) * feerate_sat_per_1000_weight as i64 / 1000 < 0 {
                Err(())
        } else {
                Ok(starting_weight)
@@ -71,26 +72,27 @@ pub(crate) fn maybe_add_change_output(tx: &mut Transaction, input_value: u64, wi
 mod tests {
        use super::*;
 
+       use bitcoin::amount::Amount;
        use bitcoin::blockdata::locktime::absolute::LockTime;
-       use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, OutPoint};
-       use bitcoin::blockdata::script::{ScriptBuf, Builder};
-       use bitcoin::hash_types::{PubkeyHash, Txid};
+       use bitcoin::blockdata::transaction::{TxIn, OutPoint, Version};
+       use bitcoin::blockdata::script::Builder;
+       use bitcoin::hash_types::Txid;
        use bitcoin::hashes::Hash;
        use bitcoin::hashes::hex::FromHex;
-       use bitcoin::{Sequence, Witness};
+       use bitcoin::{PubkeyHash, Sequence, Witness};
 
        use alloc::vec;
 
        #[test]
        fn sort_output_by_value() {
                let txout1 = TxOut {
-                       value:  100,
+                       value:  Amount::from_sat(100),
                        script_pubkey: Builder::new().push_int(0).into_script()
                };
                let txout1_ = txout1.clone();
 
                let txout2 = TxOut {
-                       value: 99,
+                       value: Amount::from_sat(99),
                        script_pubkey: Builder::new().push_int(0).into_script()
                };
                let txout2_ = txout2.clone();
@@ -107,13 +109,13 @@ mod tests {
        #[test]
        fn sort_output_by_script_pubkey() {
                let txout1 = TxOut {
-                       value:  100,
+                       value:  Amount::from_sat(100),
                        script_pubkey: Builder::new().push_int(3).into_script(),
                };
                let txout1_ = txout1.clone();
 
                let txout2 = TxOut {
-                       value: 100,
+                       value: Amount::from_sat(100),
                        script_pubkey: Builder::new().push_int(1).push_int(2).into_script()
                };
                let txout2_ = txout2.clone();
@@ -130,14 +132,14 @@ mod tests {
        #[test]
        fn sort_output_by_bip_test() {
                let txout1 = TxOut {
-                       value: 100000000,
+                       value: Amount::from_sat(100000000),
                        script_pubkey: script_from_hex("41046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac")
                };
                let txout1_ = txout1.clone();
 
                // doesn't deserialize cleanly:
                let txout2 = TxOut {
-                       value: 2400000000,
+                       value: Amount::from_sat(2400000000),
                        script_pubkey: script_from_hex("41044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac")
                };
                let txout2_ = txout2.clone();
@@ -151,7 +153,7 @@ mod tests {
        #[test]
        fn sort_output_tie_breaker_test() {
                let txout1 = TxOut {
-                       value:  100,
+                       value:  Amount::from_sat(100),
                        script_pubkey: Builder::new().push_int(1).push_int(2).into_script()
                };
                let txout1_ = txout1.clone();
@@ -180,7 +182,7 @@ mod tests {
                                        let expected_raw: Vec<(u64, &str)> = $value;
                                        let expected: Vec<(TxOut, &str)> = expected_raw.iter()
                                                .map(|txout_raw| TxOut {
-                                                       value: txout_raw.0,
+                                                       value: Amount::from_sat(txout_raw.0),
                                                        script_pubkey: script_from_hex(txout_raw.1)
                                                }).map(|txout| (txout, "ignore"))
                                        .collect();
@@ -213,41 +215,43 @@ mod tests {
        #[test]
        fn test_tx_value_overrun() {
                // If we have a bogus input amount or outputs valued more than inputs, we should fail
-               let mut tx = Transaction { version: 2, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
-                       script_pubkey: ScriptBuf::new(), value: 1000
+               let mut tx = Transaction { version: Version::TWO, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
+                       script_pubkey: ScriptBuf::new(), value: Amount::from_sat(1000)
                }] };
-               assert!(maybe_add_change_output(&mut tx, 21_000_000_0000_0001, 0, 253, ScriptBuf::new()).is_err());
-               assert!(maybe_add_change_output(&mut tx, 400, 0, 253, ScriptBuf::new()).is_err());
-               assert!(maybe_add_change_output(&mut tx, 4000, 0, 253, ScriptBuf::new()).is_ok());
+               assert!(maybe_add_change_output(&mut tx, Amount::from_sat(21_000_000_0000_0001), 0, 253, ScriptBuf::new()).is_err());
+               assert!(maybe_add_change_output(&mut tx, Amount::from_sat(400), 0, 253, ScriptBuf::new()).is_err());
+               assert!(maybe_add_change_output(&mut tx, Amount::from_sat(4000), 0, 253, ScriptBuf::new()).is_ok());
        }
 
        #[test]
        fn test_tx_change_edge() {
                // Check that we never add dust outputs
-               let mut tx = Transaction { version: 2, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
+               let mut tx = Transaction { version: Version::TWO, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
                let orig_wtxid = tx.wtxid();
                let output_spk = ScriptBuf::new_p2pkh(&PubkeyHash::hash(&[0; 0]));
                assert_eq!(output_spk.dust_value().to_sat(), 546);
-               // 9 sats isn't enough to pay fee on a dummy transaction...
-               assert_eq!(tx.weight().to_wu(), 40); // ie 10 vbytes
-               assert!(maybe_add_change_output(&mut tx, 9, 0, 250, output_spk.clone()).is_err());
+               // base size = version size + varint[input count] + input size + varint[output count] + output size + lock time size
+               // total size = version size + marker + flag + varint[input count] + input size + varint[output count] + output size + lock time size
+               // weight = 3 * base size + total size = 3 * (4 + 1 + 0 + 1 + 0 + 4) + (4 + 1 + 1 + 1 + 0 + 1 + 0 + 4) = 3 * 10 + 12 = 42
+               assert_eq!(tx.weight().to_wu(), 42);
+               // 10 sats isn't enough to pay fee on a dummy transaction...
+               assert!(maybe_add_change_output(&mut tx, Amount::from_sat(10), 0, 250, output_spk.clone()).is_err());
                assert_eq!(tx.wtxid(), orig_wtxid); // Failure doesn't change the transaction
-               // but 10-564 is, just not enough to add a change output...
-               assert!(maybe_add_change_output(&mut tx, 10, 0, 250, output_spk.clone()).is_ok());
+               // but 11 (= ceil(42 * 250 / 1000)) is, just not enough to add a change output...
+               assert!(maybe_add_change_output(&mut tx, Amount::from_sat(11), 0, 250, output_spk.clone()).is_ok());
                assert_eq!(tx.output.len(), 0);
                assert_eq!(tx.wtxid(), orig_wtxid); // If we don't add an output, we don't change the transaction
-               assert!(maybe_add_change_output(&mut tx, 549, 0, 250, output_spk.clone()).is_ok());
+               assert!(maybe_add_change_output(&mut tx, Amount::from_sat(549), 0, 250, output_spk.clone()).is_ok());
                assert_eq!(tx.output.len(), 0);
                assert_eq!(tx.wtxid(), orig_wtxid); // If we don't add an output, we don't change the transaction
-               // 590 is also not enough, if we anticipate 2 more weight units pushing us up to the next vbyte
-               // (considering the two bytes for segwit flags)
-               assert!(maybe_add_change_output(&mut tx, 590, 2, 250, output_spk.clone()).is_ok());
+               // 590 is also not enough
+               assert!(maybe_add_change_output(&mut tx, Amount::from_sat(590), 0, 250, output_spk.clone()).is_ok());
                assert_eq!(tx.output.len(), 0);
                assert_eq!(tx.wtxid(), orig_wtxid); // If we don't add an output, we don't change the transaction
-               // at 590 we can afford the change output at the dust limit (546)
-               assert!(maybe_add_change_output(&mut tx, 590, 0, 250, output_spk.clone()).is_ok());
+               // at 591 we can afford the change output at the dust limit (546)
+               assert!(maybe_add_change_output(&mut tx, Amount::from_sat(591), 0, 250, output_spk.clone()).is_ok());
                assert_eq!(tx.output.len(), 1);
-               assert_eq!(tx.output[0].value, 546);
+               assert_eq!(tx.output[0].value.to_sat(), 546);
                assert_eq!(tx.output[0].script_pubkey, output_spk);
                assert_eq!(tx.weight().to_wu() / 4, 590-546); // New weight is exactly the fee we wanted.
 
@@ -258,10 +262,10 @@ mod tests {
        #[test]
        fn test_tx_extra_outputs() {
                // Check that we correctly handle existing outputs
-               let mut tx = Transaction { version: 2, lock_time: LockTime::ZERO, input: vec![TxIn {
+               let mut tx = Transaction { version: Version::TWO, lock_time: LockTime::ZERO, input: vec![TxIn {
                        previous_output: OutPoint::new(Txid::all_zeros(), 0), script_sig: ScriptBuf::new(), witness: Witness::new(), sequence: Sequence::ZERO,
                }], output: vec![TxOut {
-                       script_pubkey: Builder::new().push_int(1).into_script(), value: 1000
+                       script_pubkey: Builder::new().push_int(1).into_script(), value: Amount::from_sat(1000)
                }] };
                let orig_wtxid = tx.wtxid();
                let orig_weight = tx.weight().to_wu();
@@ -270,18 +274,18 @@ mod tests {
                assert_eq!(Builder::new().push_int(2).into_script().dust_value().to_sat(), 474);
 
                // Input value of the output value + fee - 1 should fail:
-               assert!(maybe_add_change_output(&mut tx, 1000 + 61 + 100 - 1, 400, 250, Builder::new().push_int(2).into_script()).is_err());
+               assert!(maybe_add_change_output(&mut tx, Amount::from_sat(1000 + 61 + 100 - 1), 400, 250, Builder::new().push_int(2).into_script()).is_err());
                assert_eq!(tx.wtxid(), orig_wtxid); // Failure doesn't change the transaction
                // but one more input sat should succeed, without changing the transaction
-               assert!(maybe_add_change_output(&mut tx, 1000 + 61 + 100, 400, 250, Builder::new().push_int(2).into_script()).is_ok());
+               assert!(maybe_add_change_output(&mut tx, Amount::from_sat(1000 + 61 + 100), 400, 250, Builder::new().push_int(2).into_script()).is_ok());
                assert_eq!(tx.wtxid(), orig_wtxid); // If we don't add an output, we don't change the transaction
                // In order to get a change output, we need to add 474 plus the output's weight / 4 (10)...
-               assert!(maybe_add_change_output(&mut tx, 1000 + 61 + 100 + 474 + 9, 400, 250, Builder::new().push_int(2).into_script()).is_ok());
+               assert!(maybe_add_change_output(&mut tx, Amount::from_sat(1000 + 61 + 100 + 474 + 9), 400, 250, Builder::new().push_int(2).into_script()).is_ok());
                assert_eq!(tx.wtxid(), orig_wtxid); // If we don't add an output, we don't change the transaction
 
-               assert!(maybe_add_change_output(&mut tx, 1000 + 61 + 100 + 474 + 10, 400, 250, Builder::new().push_int(2).into_script()).is_ok());
+               assert!(maybe_add_change_output(&mut tx, Amount::from_sat(1000 + 61 + 100 + 474 + 10), 400, 250, Builder::new().push_int(2).into_script()).is_ok());
                assert_eq!(tx.output.len(), 2);
-               assert_eq!(tx.output[1].value, 474);
+               assert_eq!(tx.output[1].value.to_sat(), 474);
                assert_eq!(tx.output[1].script_pubkey, Builder::new().push_int(2).into_script());
                assert_eq!(tx.weight().to_wu() - orig_weight, 40); // Weight difference matches what we had to add above
                tx.output.pop();
index b2c9d21b998e790dba23570d49be13a2be6d2053..c95b3dbef3d90e66b118f92790256073a2f67dce 100644 (file)
@@ -17,6 +17,7 @@ use alloc::sync::Arc;
 use core::mem;
 use crate::sync::Mutex;
 
+#[allow(unused_imports)]
 use crate::prelude::*;
 
 #[cfg(feature = "std")]
@@ -305,7 +306,7 @@ mod tests {
        use super::*;
        use core::sync::atomic::{AtomicBool, Ordering};
        use core::future::Future as FutureTrait;
-       use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
+       use core::task::{RawWaker, RawWakerVTable};
 
        #[test]
        fn notifier_pre_notified_future() {
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8b137891791fe96927ad78e64b0aad7bded08bdc 100644 (file)
@@ -0,0 +1 @@
+
index e02b59669b10fbd51e35295bcca36037758e86f7..5d8a35ccdf58e408454cfc0be4354a260618859b 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "possiblyrandom"
-version = "0.1.0"
+version = "0.2.0"
 authors = ["Matt Corallo"]
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/lightningdevkit/rust-lightning/"
index a6f59779e10c9232ef6543bff6c2895bef0710c4..36d85ef2061b4056d24c268ec3d1157cc9f5a574 100644 (file)
@@ -3,6 +3,7 @@
 ./fuzz/src/bech32_parse.rs
 ./fuzz/src/bin/base32_target.rs
 ./fuzz/src/bin/bech32_parse_target.rs
+./fuzz/src/bin/bolt11_deser_target.rs
 ./fuzz/src/bin/chanmon_consistency_target.rs
 ./fuzz/src/bin/chanmon_deser_target.rs
 ./fuzz/src/bin/fromstr_to_netaddress_target.rs
 ./lightning/src/ln/chanmon_update_fail_tests.rs
 ./lightning/src/ln/channel.rs
 ./lightning/src/ln/channel_id.rs
-./lightning/src/ln/channel_keys.rs
 ./lightning/src/ln/channelmanager.rs
 ./lightning/src/ln/features.rs
 ./lightning/src/ln/functional_test_utils.rs
 ./lightning/src/ln/functional_tests.rs
 ./lightning/src/ln/inbound_payment.rs
+./lightning/src/ln/max_payment_path_len_tests.rs
 ./lightning/src/ln/mod.rs
 ./lightning/src/ln/monitor_tests.rs
 ./lightning/src/ln/msgs.rs
 ./lightning/src/ln/reorg_tests.rs
 ./lightning/src/ln/script.rs
 ./lightning/src/ln/shutdown_tests.rs
+./lightning/src/ln/types.rs
 ./lightning/src/ln/wire.rs
 ./lightning/src/offers/invoice.rs
 ./lightning/src/offers/invoice_error.rs
 ./lightning/src/routing/scoring.rs
 ./lightning/src/routing/test_utils.rs
 ./lightning/src/routing/utxo.rs
-./lightning/src/sign/ecdsa.rs
-./lightning/src/sign/mod.rs
-./lightning/src/sign/taproot.rs
-./lightning/src/sign/type_resolver.rs
 ./lightning/src/sync/debug_sync.rs
 ./lightning/src/sync/fairrwlock.rs
 ./lightning/src/sync/mod.rs
 ./lightning/src/util/time.rs
 ./lightning/src/util/transaction_utils.rs
 ./lightning/src/util/wakers.rs
-./msrv-no-dev-deps-check/src/lib.rs
-./no-std-check/src/lib.rs