Merge pull request #2981 from dunxen/2024-03-PR2419fixups
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Tue, 23 Apr 2024 13:48:40 +0000 (06:48 -0700)
committerGitHub <noreply@github.com>
Tue, 23 Apr 2024 13:48:40 +0000 (06:48 -0700)
Fixups for #2419

46 files changed:
ci/check-cfg-flags.py
ci/ci-tests.sh
fuzz/src/chanmon_consistency.rs
fuzz/src/utils/test_persister.rs
lightning-background-processor/Cargo.toml
lightning-background-processor/src/lib.rs
lightning-block-sync/Cargo.toml
lightning-custom-message/Cargo.toml
lightning-invoice/Cargo.toml
lightning-net-tokio/Cargo.toml
lightning-net-tokio/src/lib.rs
lightning-persister/Cargo.toml
lightning-rapid-gossip-sync/Cargo.toml
lightning-transaction-sync/Cargo.toml
lightning/Cargo.toml
lightning/src/blinded_path/payment.rs
lightning/src/chain/chaininterface.rs
lightning/src/chain/chainmonitor.rs
lightning/src/chain/channelmonitor.rs
lightning/src/chain/mod.rs
lightning/src/events/mod.rs
lightning/src/ln/blinded_payment_tests.rs
lightning/src/ln/chanmon_update_fail_tests.rs
lightning/src/ln/channel.rs
lightning/src/ln/channel_keys.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_test_utils.rs
lightning/src/ln/functional_tests.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/payment_tests.rs
lightning/src/ln/peer_handler.rs
lightning/src/ln/reorg_tests.rs
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/sign/mod.rs
lightning/src/util/mod.rs
lightning/src/util/persist.rs
lightning/src/util/sweep.rs [new file with mode: 0644]
lightning/src/util/test_utils.rs

index 0cfa2023ee2b9844a07601548584d7c687f43e04..fd514da657bfd8a12801eea16a9cf8fedd13aac6 100755 (executable)
@@ -98,6 +98,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 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 36e7cea8a2215e1414af4ef0f46a082401732c03..b3cf867d6e330d5e684c78f2406c815d0a284a04 100644 (file)
@@ -84,7 +84,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),
                }
        }
index 89de25aa5e6a192786a36b8d97605dbabf86b25d..f9a03d16178ac224bce66333d14fc8d544124310 100644 (file)
@@ -17,4 +17,7 @@ impl chainmonitor::Persist<TestChannelSigner> for TestPersister {
        fn update_persisted_channel(&self, _funding_txo: OutPoint, _update: Option<&channelmonitor::ChannelMonitorUpdate>, _data: &channelmonitor::ChannelMonitor<TestChannelSigner>, _update_id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus {
                self.update_ret.lock().unwrap().clone()
        }
+
+       fn archive_persisted_channel(&self, _: OutPoint) {
+       }
 }
index 1872a1d0714ca73127521e89e073b4fbb0c612f0..5148bb81ea48a7021f3541fa647f644bd1b87a3e 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"
@@ -22,11 +22,11 @@ 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 }
+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 3736bd603e5bd65977bfc874919b5f9c61ee48bc..3fe4da00d4bcc85a07a14e56969d3ba76c03cf32 100644 (file)
@@ -919,14 +919,16 @@ impl Drop for BackgroundProcessor {
 
 #[cfg(all(feature = "std", test))]
 mod tests {
+       use bitcoin::{ScriptBuf, Txid};
        use bitcoin::blockdata::constants::{genesis_block, ChainHash};
        use bitcoin::blockdata::locktime::absolute::LockTime;
        use bitcoin::blockdata::transaction::{Transaction, TxOut};
+       use bitcoin::hashes::Hash;
        use bitcoin::network::constants::Network;
        use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1};
-       use lightning::chain::{BestBlock, Confirm, chainmonitor};
+       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};
@@ -947,6 +949,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};
@@ -1009,6 +1012,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 {
@@ -1247,6 +1253,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);
@@ -1271,10 +1285,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 {
@@ -1283,7 +1302,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);
                }
 
@@ -1352,15 +1371,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);
        }
@@ -1592,6 +1636,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();
@@ -1617,10 +1664,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));
+                       },
                        _ => 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..c55281f6ccc4b59e7ab0c1bb9690a4371717fb5c 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"
@@ -20,11 +20,11 @@ rpc-client = [ "serde_json", "chunked_transfer" ]
 [dependencies]
 bitcoin = "0.30.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 fbec38431c42acc209e1756bbc7b1c1dc8bbc964..6b150280b6016e0794896371af993473a7a8a825 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"
@@ -15,4 +15,4 @@ rustdoc-args = ["--cfg", "docsrs"]
 
 [dependencies]
 bitcoin = "0.30.2"
-lightning = { version = "0.0.121", path = "../lightning" }
+lightning = { version = "0.0.123-beta", path = "../lightning" }
index 1692988ba0b0c71913b7307d47f69d6a595d9d2f..1ebf67be1ad43f78776ed6e8e187ee6e2bace16b 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"
@@ -21,13 +21,13 @@ 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 }
+lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false }
 secp256k1 = { version = "0.27.0", default-features = false, features = ["recovery", "alloc"] }
 serde = { version = "1.0.118", optional = true }
 bitcoin = { version = "0.30.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 }
index f779a4395b768d242af25445a6a1c9fc0f520d6c..0ab9f82f527e4b79327576c173c4e8f6b6ab8c2c 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/"
@@ -16,9 +16,9 @@ rustdoc-args = ["--cfg", "docsrs"]
 
 [dependencies]
 bitcoin = "0.30.2"
-lightning = { version = "0.0.121", path = "../lightning" }
+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 98932c0eaa638d86a9fd9faefc271fc5b9ceb97c..6d001ca67fd5e1ec44d0307a198509d83cf2538a 100644 (file)
@@ -624,11 +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(dual_funding)]
+               #[cfg(splicing)]
                fn handle_splice(&self, _their_node_id: &PublicKey, _msg: &Splice) {}
-               #[cfg(dual_funding)]
+               #[cfg(splicing)]
                fn handle_splice_ack(&self, _their_node_id: &PublicKey, _msg: &SpliceAck) {}
-               #[cfg(dual_funding)]
+               #[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..9f7aca47a785ebf8c4ceaeea4fc816342d4d175b 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"
@@ -15,7 +15,7 @@ rustdoc-args = ["--cfg", "docsrs"]
 
 [dependencies]
 bitcoin = "0.30.2"
-lightning = { version = "0.0.121", path = "../lightning" }
+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"] }
+lightning = { version = "0.0.123-beta", path = "../lightning", features = ["_test_utils"] }
 bitcoin = { version = "0.30.2", default-features = false }
index 5da607c9fa018bae76340bedbc4bd9454d4302e9..02ff3da57b5cc68d95cdf62cdbbd2b433b3754de 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 }
+lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false }
 bitcoin = { version = "0.30.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 4118d3012ef5e4547de5ac165ac9e2382bc6ab90..8bb4958f9fa22183276be848fecc1262812c32e4 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,7 +23,7 @@ electrum = ["electrum-client"]
 async-interface = []
 
 [dependencies]
-lightning = { version = "0.0.121", path = "../lightning", default-features = false, features = ["std"] }
+lightning = { version = "0.0.123-beta", path = "../lightning", default-features = false, features = ["std"] }
 bitcoin = { version = "0.30.2", default-features = false }
 bdk-macros = "0.6"
 futures = { version = "0.3", optional = true }
@@ -31,7 +31,7 @@ esplora-client = { version = "0.6", default-features = false, optional = true }
 electrum-client = { version = "0.18.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]
index 96070e4d0dd23246976b22004954d38704cddeb9..27f7af93fdb87284e7ea889d7d03e80a988392e7 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/"
index 3d56020a626d68cd1e233f53a905cfdfa8acf21c..ec441c18c986ed4788948dcf09465e07233878d8 100644 (file)
@@ -12,6 +12,8 @@ use crate::ln::channelmanager::CounterpartyForwardingInfo;
 use crate::ln::features::BlindedHopFeatures;
 use crate::ln::msgs::DecodeError;
 use crate::offers::invoice::BlindedPayInfo;
+use crate::offers::invoice_request::InvoiceRequestFields;
+use crate::offers::offer::OfferId;
 use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, Writeable, Writer};
 
 #[allow(unused_imports)]
@@ -53,6 +55,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 +101,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 = ();
 
@@ -137,7 +201,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(())
        }
@@ -163,11 +228,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)?,
@@ -179,6 +247,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(),
                        }))
                }
        }
@@ -309,10 +378,53 @@ 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::blinded_path::payment::{ForwardNode, ForwardTlvs, ReceiveTlvs, PaymentConstraints, PaymentContext, PaymentRelay};
        use crate::ln::PaymentSecret;
        use crate::ln::features::BlindedHopFeatures;
        use crate::ln::functional_test_utils::TEST_FINAL_CLTV;
@@ -361,6 +473,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();
@@ -379,6 +492,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);
@@ -432,6 +546,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();
@@ -482,6 +597,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());
@@ -536,6 +652,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 2bf6d6130e1123eef41a77fd3e5ffd1e5e3b72be..2e37127e038548ebe86888e185f52af1b9264551 100644 (file)
@@ -124,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
index 015b3dacfc3db6cc0e5a526729c6731d2c4c832f..81ba30ffb83ed964ca4fe13901be979073e2e6ad 100644 (file)
@@ -194,6 +194,11 @@ pub trait Persist<ChannelSigner: WriteableEcdsaChannelSigner> {
        ///
        /// [`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;
+       /// 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> {
@@ -363,8 +368,9 @@ where C::Target: chain::Filter,
                let mut txn_outputs;
                {
                        txn_outputs = process(monitor, txdata);
+                       let chain_sync_update_id = self.sync_persistence_id.get_increment();
                        let update_id = MonitorUpdateId {
-                               contents: UpdateOrigin::ChainSync(self.sync_persistence_id.get_increment()),
+                               contents: UpdateOrigin::ChainSync(chain_sync_update_id),
                        };
                        let mut pending_monitor_updates = monitor_state.pending_monitor_updates.lock().unwrap();
                        if let Some(height) = best_height {
@@ -376,10 +382,16 @@ where C::Target: chain::Filter,
                                }
                        }
 
-                       log_trace!(logger, "Syncing Channel Monitor for channel {}", log_funding_info!(monitor));
+                       log_trace!(logger, "Syncing Channel Monitor for channel {} for block-data update_id {}",
+                               log_funding_info!(monitor),
+                               chain_sync_update_id
+                       );
                        match self.persister.update_persisted_channel(*funding_outpoint, None, monitor, update_id) {
                                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 update_id {}",
+                                               log_funding_info!(monitor),
+                                               chain_sync_update_id
+                                       ),
                                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);
@@ -529,7 +541,7 @@ where C::Target: chain::Filter,
                pending_monitor_updates.retain(|update_id| *update_id != completed_update_id);
 
                match completed_update_id {
-                       MonitorUpdateId { contents: UpdateOrigin::OffChain(_) } => {
+                       MonitorUpdateId { contents: UpdateOrigin::OffChain(completed_update_id) } => {
                                // 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
@@ -540,6 +552,14 @@ where C::Target: chain::Filter,
                                // `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);
+                               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.
@@ -551,8 +571,18 @@ where C::Target: chain::Filter,
                                        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) {
+                       MonitorUpdateId { contents: UpdateOrigin::ChainSync(completed_update_id) } => {
+                               let monitor_has_pending_updates =
+                                       monitor_data.has_pending_chainsync_updates(&pending_monitor_updates);
+                               log_debug!(self.logger, "Completed chain sync monitor update {} for channel with funding outpoint {:?}, {}",
+                                       completed_update_id,
+                                       funding_txo,
+                                       if monitor_has_pending_updates {
+                                               "still have pending chain sync updates"
+                                       } else {
+                                               "all chain sync updates complete, releasing pending MonitorEvents"
+                                       });
+                               if !monitor_has_pending_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.
@@ -656,6 +686,41 @@ 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);
+                       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);
+                               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>
@@ -804,7 +869,7 @@ 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));
+                               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);
@@ -823,10 +888,18 @@ where C::Target: chain::Filter,
                                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
index c2d3a43b1f4bd40d65ea041478313a878d49d7ca..66d083377b0e6c23b5f863adb1c2f2aa0b7f98a4 100644 (file)
@@ -935,6 +935,9 @@ 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.
@@ -1145,6 +1148,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(())
@@ -1328,6 +1332,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                        best_block,
                        counterparty_node_id: Some(counterparty_node_id),
                        initial_counterparty_commitment_info: None,
+                       balances_empty_height: None,
                })
        }
 
@@ -1856,6 +1861,52 @@ 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
+       /// 2016 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;
+                       }
+               }
+
+               match (inner.balances_empty_height, is_all_funds_claimed) {
+                       (Some(balances_empty_height), true) => {
+                               // Claimed all funds, check if reached the blocks threshold.
+                               const BLOCKS_THRESHOLD: u32 = 4032; // ~four weeks
+                               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.
+                               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()
@@ -4330,6 +4381,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()),
                                        }));
                                }
                        }
@@ -4632,6 +4684,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),
@@ -4644,6 +4697,7 @@ 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
@@ -4722,6 +4776,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
                        best_block,
                        counterparty_node_id,
                        initial_counterparty_commitment_info,
+                       balances_empty_height,
                })))
        }
 }
index e22ccca986aacf622778c7c4c3c5320b3815e341..1fb30a9aeb5f9375b4e1ecdac2950aea62a9ab37 100644 (file)
@@ -20,6 +20,7 @@ use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, Monitor
 use crate::ln::ChannelId;
 use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
 use crate::chain::transaction::{OutPoint, TransactionData};
+use crate::impl_writeable_tlv_based;
 
 #[allow(unused_imports)]
 use crate::prelude::*;
@@ -56,6 +57,11 @@ impl BestBlock {
        }
 }
 
+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.
index f6e7f7164874f0de97fb56a8dc3c4294d2206c94..e72bc0228fd18fe43319744c550e295c5efa5fe7 100644 (file)
@@ -18,6 +18,7 @@ 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;
@@ -49,11 +50,12 @@ use crate::prelude::*;
 /// 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
@@ -70,6 +72,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),
@@ -79,17 +123,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)
 );
 
@@ -792,9 +886,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>,
@@ -912,8 +1012,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
@@ -1058,10 +1158,27 @@ 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);
@@ -1081,6 +1198,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 } => {
@@ -1311,6 +1429,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),
@@ -1323,12 +1442,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),
                                        };
index eb31be9eecf334ead7ece1d1d6ecd7b587534f61..a0438b2447a004a1fc2261c036c673d363968a37 100644 (file)
@@ -9,7 +9,7 @@
 
 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::channelmanager;
@@ -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,
@@ -1281,6 +1284,7 @@ fn custom_tlvs_to_blinded_path() {
                        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(
index 2e95f5c63ff7e173bb5a107f0719fb89ca3e3301..11ab1fbb85158a40f6e796ab1b26856df16c8458 100644 (file)
@@ -173,11 +173,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 +554,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 +672,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 +1683,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 +1699,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"),
index 58d35a1a95a4ddfc05b29d7fcfe4db5bdf6e89a7..c02659cd0b01cc83548b988c362929074c56808d 100644 (file)
@@ -1189,9 +1189,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>),
 }
@@ -1205,9 +1205,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,
                }
        }
@@ -1217,9 +1217,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,
                }
        }
@@ -3501,7 +3501,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);
@@ -3524,7 +3524,7 @@ pub(crate) fn commit_tx_fee_msat(feerate_per_kw: u32, num_htlcs: usize, channel_
 }
 
 /// 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,
@@ -3541,7 +3541,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>,
 }
 
@@ -7704,7 +7704,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,
                };
 
@@ -7994,7 +7994,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();
@@ -8005,15 +8005,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,
@@ -8129,14 +8129,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!
@@ -9303,7 +9303,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,
                })
        }
index b577dc60008583537c4d9c1cebc8b2f6e5f48e77..423d410740720dc2bb5b4e12aa39cf5d1bf4c2ca 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)*) => {
@@ -37,6 +37,20 @@ macro_rules! basepoint_impl {
                        pub fn to_public_key(&self) -> PublicKey {
                                self.0
                        }
+
+                       /// Derives a per-commitment-transaction (eg an htlc key or delayed_payment key) private key addition tweak
+                       /// from a basepoint and a per_commitment_point:
+                       /// `privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)`
+                       /// This calculates the hash part in the tweak derivation process, which is used to ensure
+                       /// that each key is unique and cannot be guessed by an external party. It is equivalent
+                       /// to the `from_basepoint` method, but without the addition operation, providing just the
+                       /// tweak from the hash of the per_commitment_point and the basepoint.
+                       pub fn derive_add_tweak(&self, per_commitment_point: &PublicKey) -> [u8; 32] {
+                               let mut sha = Sha256::engine();
+                               sha.input(&per_commitment_point.serialize());
+                               sha.input(&self.to_public_key().serialize());
+                               Sha256::from_engine(sha).to_byte_array()
+                       }
                }
 
                impl From<PublicKey> for $BasepointT {
@@ -44,8 +58,7 @@ macro_rules! basepoint_impl {
                                Self(value)
                        }
                }
-
-       }
+       };
 }
 macro_rules! key_impl {
        ($BasepointT:ty, $KeyName:expr) => {
@@ -87,11 +100,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
@@ -102,7 +113,6 @@ pub struct DelayedPaymentBasepoint(pub PublicKey);
 basepoint_impl!(DelayedPaymentBasepoint);
 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
@@ -150,14 +160,26 @@ 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 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.
+pub fn add_public_key_tweak<T: secp256k1::Signing>(
+       secp_ctx: &Secp256k1<T>, base_point: &PublicKey, tweak: &[u8; 32],
+) -> PublicKey {
+       let hashkey = PublicKey::from_secret_key(
+               &secp_ctx,
+               &SecretKey::from_slice(tweak)
+                       .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 +191,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 +213,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 +247,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 abbab5ff0a70d8f0d5b4b7bf58b3bf4702647566..50366faad1c18ca6a68c0b0ae42037d4ebdfacac 100644 (file)
@@ -32,7 +32,7 @@ use bitcoin::secp256k1::Secp256k1;
 use bitcoin::{secp256k1, Sequence};
 
 use crate::blinded_path::{BlindedPath, NodeIdLookUp};
-use crate::blinded_path::payment::{PaymentConstraints, ReceiveTlvs};
+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};
@@ -61,7 +61,6 @@ 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};
@@ -156,6 +155,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.
@@ -353,6 +357,11 @@ enum OnionPayload {
                /// This is only here for backwards-compatibility in serialization, in the future it can be
                /// removed, breaking clients running 0.0.106 and earlier.
                _legacy_hop_data: Option<msgs::FinalOnionHopData>,
+               /// 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>,
        },
        /// Contains the payer-provided preimage.
        Spontaneous(PaymentPreimage),
@@ -918,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,
                        }
                )
@@ -1455,12 +1464,12 @@ where
 /// // On the event processing thread
 /// channel_manager.process_pending_events(&|event| match event {
 ///     Event::PaymentClaimable { payment_hash, purpose, .. } => match purpose {
-///         PaymentPurpose::InvoicePayment { payment_preimage: Some(payment_preimage), .. } => {
+///         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::InvoicePayment { payment_preimage: None, .. } => {
+///         PaymentPurpose::Bolt11InvoicePayment { payment_preimage: None, .. } => {
 ///             println!("Unknown payment hash: {}", payment_hash);
 ///         },
 ///         PaymentPurpose::SpontaneousPayment(payment_preimage) => {
@@ -1468,6 +1477,8 @@ where
 ///             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);
@@ -1555,11 +1566,11 @@ where
 /// // On the event processing thread
 /// channel_manager.process_pending_events(&|event| match event {
 ///     Event::PaymentClaimable { payment_hash, purpose, .. } => match purpose {
-///         PaymentPurpose::InvoicePayment { payment_preimage: Some(payment_preimage), .. } => {
+///         PaymentPurpose::Bolt12OfferPayment { payment_preimage: Some(payment_preimage), .. } => {
 ///             println!("Claiming payment {}", payment_hash);
 ///             channel_manager.claim_funds(payment_preimage);
 ///         },
-///         PaymentPurpose::InvoicePayment { payment_preimage: None, .. } => {
+///         PaymentPurpose::Bolt12OfferPayment { payment_preimage: None, .. } => {
 ///             println!("Unknown payment hash: {}", payment_hash);
 ///         },
 ///         // ...
@@ -1695,25 +1706,31 @@ where
 /// #
 /// # fn example<T: AChannelManager>(channel_manager: T, refund: &Refund) {
 /// # let channel_manager = channel_manager.get_cm();
-/// match channel_manager.request_refund_payment(refund) {
-///     Ok(()) => println!("Requesting payment for refund"),
-///     Err(e) => println!("Unable to request payment for refund: {:?}", e),
-/// }
+/// 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::InvoicePayment { payment_preimage: Some(payment_preimage), .. } => {
+///            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::InvoicePayment { payment_preimage: None, .. } => {
+///            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);
 ///     },
 ///     // ...
@@ -2774,11 +2791,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)
                        },
@@ -3653,8 +3670,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
@@ -5331,13 +5348,14 @@ where
                                                                let blinded_failure = routing.blinded_failure();
                                                                let (cltv_expiry, onion_payload, payment_data, 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 },
+                                                                               (incoming_cltv_expiry, OnionPayload::Invoice { _legacy_hop_data, payment_context },
                                                                                        Some(payment_data), phantom_shared_secret, onion_fields)
                                                                        },
                                                                        PendingHTLCRouting::ReceiveKeysend {
@@ -5415,10 +5433,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);
@@ -5515,7 +5530,7 @@ where
                                                                match payment_secrets.entry(payment_hash) {
                                                                        hash_map::Entry::Vacant(_) => {
                                                                                match claimable_htlc.onion_payload {
-                                                                                       OnionPayload::Invoice { .. } => {
+                                                                                       OnionPayload::Invoice { ref payment_context, .. } => {
                                                                                                let payment_data = payment_data.unwrap();
                                                                                                let (payment_preimage, min_final_cltv_expiry_delta) = match inbound_payment::verify(payment_hash, &payment_data, self.highest_seen_timestamp.load(Ordering::Acquire) as u64, &self.inbound_payment_key, &self.logger) {
                                                                                                        Ok(result) => result,
@@ -5532,10 +5547,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.clone(),
+                                                                                                       payment_data.payment_secret,
+                                                                                                       payment_context.clone(),
+                                                                                               );
                                                                                                check_total_value!(purpose);
                                                                                        },
                                                                                        OnionPayload::Spontaneous(preimage) => {
@@ -5545,10 +5561,13 @@ where
                                                                                }
                                                                        },
                                                                        hash_map::Entry::Occupied(inbound_payment) => {
-                                                                               if let OnionPayload::Spontaneous(_) = claimable_htlc.onion_payload {
-                                                                                       log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} because we already have an inbound payment with the same payment hash", &payment_hash);
-                                                                                       fail_htlc!(claimable_htlc, payment_hash);
-                                                                               }
+                                                                               let payment_context = match claimable_htlc.onion_payload {
+                                                                                       OnionPayload::Spontaneous(_) => {
+                                                                                               log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} because we already have an inbound payment with the same payment hash", &payment_hash);
+                                                                                               fail_htlc!(claimable_htlc, payment_hash);
+                                                                                       },
+                                                                                       OnionPayload::Invoice { ref payment_context, .. } => payment_context,
+                                                                               };
                                                                                let payment_data = payment_data.unwrap();
                                                                                if inbound_payment.get().payment_secret != payment_data.payment_secret {
                                                                                        log_trace!(self.logger, "Failing new HTLC with payment_hash {} as it didn't match our expected payment secret.", &payment_hash);
@@ -5558,10 +5577,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.clone(),
+                                                                                       );
                                                                                        let payment_claimable_generated = check_total_value!(purpose);
                                                                                        if payment_claimable_generated {
                                                                                                inbound_payment.remove_entry();
@@ -5882,12 +5902,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)
@@ -7059,8 +7079,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.
@@ -7073,8 +7093,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;
@@ -7501,7 +7521,7 @@ where
                                                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);
@@ -8775,7 +8795,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
        ///
@@ -8792,7 +8812,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;
@@ -8808,7 +8830,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")]
@@ -8831,7 +8856,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),
                                        );
@@ -8847,7 +8872,7 @@ where
                                        }
                                }
 
-                               Ok(())
+                               Ok(invoice)
                        },
                        Err(()) => Err(Bolt12SemanticError::InvalidAmount),
                }
@@ -8859,10 +8884,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.
        ///
@@ -8882,8 +8906,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), ()> {
@@ -8974,7 +8997,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;
 
@@ -8988,6 +9011,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
@@ -9450,7 +9474,7 @@ where
                                                // 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);
@@ -9756,21 +9780,21 @@ where
                         msg.channel_id.clone())), *counterparty_node_id);
        }
 
-       #[cfg(dual_funding)]
+       #[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(dual_funding)]
+       #[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(dual_funding)]
+       #[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(),
@@ -9928,11 +9952,11 @@ where
                                                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
                                                },
@@ -10093,8 +10117,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(),
@@ -10109,8 +10133,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.
@@ -10213,7 +10237,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 {
@@ -10224,7 +10248,7 @@ where
                                                }
                                        },
                                        None | Some(ChannelPhase::UnfundedInboundV1(_) | ChannelPhase::Funded(_)) => (),
-                                       #[cfg(dual_funding)]
+                                       #[cfg(any(dual_funding, splicing))]
                                        Some(ChannelPhase::UnfundedInboundV2(_)) => (),
                                }
                        }
@@ -10344,8 +10368,12 @@ where
                                        },
                                };
 
+                               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(()) => {
@@ -10359,7 +10387,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
@@ -10368,12 +10396,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);
@@ -10381,47 +10407,46 @@ 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) => Some(OffersMessage::Invoice(invoice)),
+                                       Err(error) => Some(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 response {
+                                       Ok(()) => None,
+                                       Err(e) => Some(OffersMessage::InvoiceError(e)),
                                }
                        },
                        OffersMessage::InvoiceError(invoice_error) => {
@@ -10675,6 +10700,7 @@ 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),
@@ -10789,9 +10815,11 @@ 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::Spontaneous(preimage) => (None, Some(preimage)),
+               let (payment_data, keysend_preimage, payment_context) = match &self.onion_payload {
+                       OnionPayload::Invoice { _legacy_hop_data, payment_context } => {
+                               (_legacy_hop_data.as_ref(), None, payment_context.as_ref())
+                       },
+                       OnionPayload::Spontaneous(preimage) => (None, Some(preimage), None),
                };
                write_tlv_fields!(writer, {
                        (0, self.prev_hop, required),
@@ -10803,6 +10831,7 @@ impl Writeable for ClaimableHTLC {
                        (6, self.cltv_expiry, required),
                        (8, keysend_preimage, option),
                        (10, self.counterparty_skimmed_fee_msat, option),
+                       (11, payment_context, option),
                });
                Ok(())
        }
@@ -10820,6 +10849,7 @@ impl Readable for ClaimableHTLC {
                        (6, cltv_expiry, required),
                        (8, keysend_preimage, option),
                        (10, counterparty_skimmed_fee_msat, option),
+                       (11, payment_context, option),
                });
                let payment_data: Option<msgs::FinalOnionHopData> = payment_data_opt;
                let value = value_ser.0.unwrap();
@@ -10840,7 +10870,7 @@ impl Readable for ClaimableHTLC {
                                        }
                                        total_msat = Some(payment_data.as_ref().unwrap().total_msat);
                                }
-                               OnionPayload::Invoice { _legacy_hop_data: payment_data }
+                               OnionPayload::Invoice { _legacy_hop_data: payment_data, payment_context }
                        },
                };
                Ok(Self {
@@ -11035,9 +11065,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();
@@ -11084,8 +11115,6 @@ where
                        decode_update_add_htlcs_opt = Some(decode_update_add_htlcs);
                }
 
-               let per_peer_state = self.per_peer_state.write().unwrap();
-
                let pending_inbound_payments = self.pending_inbound_payments.lock().unwrap();
                let claimable_payments = self.claimable_payments.lock().unwrap();
                let pending_outbound_payments = self.pending_outbound_payments.pending_outbound_payments.lock().unwrap();
@@ -11859,6 +11888,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);
                                }
                        }
@@ -12077,9 +12107,9 @@ where
                                        return Err(DecodeError::InvalidValue);
                                }
                                let purpose = match &htlcs[0].onion_payload {
-                                       OnionPayload::Invoice { _legacy_hop_data } => {
+                                       OnionPayload::Invoice { _legacy_hop_data, payment_context: _ } => {
                                                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) {
index ee4c027a10ec56c8ceb02beaf99bced52b7f71b4..cf52d946e34e0832e640b9d175dc1e98fff34cf4 100644 (file)
@@ -2129,7 +2129,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);
                                },
@@ -2606,7 +2614,17 @@ pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option<Event>
                                                assert!(onion_fields.is_some());
                                                assert_eq!(onion_fields.as_ref().unwrap().custom_tlvs, custom_tlvs);
                                                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);
@@ -2763,14 +2781,12 @@ 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,
                        ..
                } => {
                        assert_eq!(preimage, our_payment_preimage);
@@ -2780,7 +2796,9 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg
                        fwd_amt_msat = amount_msat;
                },
                Event::PaymentClaimed {
-                       purpose: PaymentPurpose::InvoicePayment { .. },
+                       purpose: PaymentPurpose::Bolt11InvoicePayment { .. }
+                               | PaymentPurpose::Bolt12OfferPayment { .. }
+                               | PaymentPurpose::Bolt12RefundPayment { .. },
                        payment_hash,
                        amount_msat,
                        ref htlcs,
index 8dd3f1fc9124f9ed72b105b14dff7b0ee9e858af..465d6288d9d3e764662edb2276d7bb5bef477cb3 100644 (file)
@@ -17,7 +17,7 @@ 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::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};
@@ -2039,11 +2039,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"),
@@ -2055,11 +2055,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"),
@@ -3954,11 +3954,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"),
@@ -4319,11 +4319,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"),
@@ -8388,10 +8388,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"),
@@ -9951,9 +9951,9 @@ fn do_test_max_dust_htlc_exposure(dust_outbound_balance: bool, exposure_breach_e
        let dust_outbound_htlc_on_holder_tx: u64 = max_dust_htlc_exposure_msat / dust_outbound_htlc_on_holder_tx_msat;
 
        // 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 
+       // 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_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;
 
        let dust_htlc_on_counterparty_tx: u64 = 4;
index d5f0dc153fc1a1ba9c544c4cfeb552708f860e04..30615b86d2f6560057276370770446ce2c99881b 100644 (file)
@@ -9,7 +9,7 @@
 
 //! 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};
@@ -158,6 +158,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
index cbdb29bfc7c735478c89f3064f95d21d3fcf0e1a..136ed4d317b35bfdc43618a0109e7c3ed1a729a1 100644 (file)
@@ -1462,13 +1462,13 @@ pub trait ChannelMessageHandler : MessageSendEventsProvider {
 
        // Splicing
        /// Handle an incoming `splice` message from the given peer.
-       #[cfg(dual_funding)]
+       #[cfg(splicing)]
        fn handle_splice(&self, their_node_id: &PublicKey, msg: &Splice);
        /// Handle an incoming `splice_ack` message from the given peer.
-       #[cfg(dual_funding)]
+       #[cfg(splicing)]
        fn handle_splice_ack(&self, their_node_id: &PublicKey, msg: &SpliceAck);
        /// Handle an incoming `splice_locked` message from the given peer.
-       #[cfg(dual_funding)]
+       #[cfg(splicing)]
        fn handle_splice_locked(&self, their_node_id: &PublicKey, msg: &SpliceLocked);
 
        // Interactive channel construction
@@ -1677,7 +1677,7 @@ pub struct FinalOnionHopData {
 
 mod fuzzy_internal_msgs {
        use bitcoin::secp256k1::PublicKey;
-       use crate::blinded_path::payment::{PaymentConstraints, PaymentRelay};
+       use crate::blinded_path::payment::{PaymentConstraints, PaymentContext, PaymentRelay};
        use crate::ln::{PaymentPreimage, PaymentSecret};
        use crate::ln::features::BlindedHopFeatures;
        use super::{FinalOnionHopData, TrampolineOnionPacket};
@@ -1716,6 +1716,7 @@ 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>)>,
@@ -2720,7 +2721,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 {
@@ -2729,6 +2730,7 @@ 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,
index 4c8c1ecba038568d1fc690b8a18f1adfce0e6d56..75a2e290f39e824514fdaa3b6333eb20b319663e 100644 (file)
 use bitcoin::network::constants::Network;
 use core::time::Duration;
 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::features::InvoiceRequestFeatures;
 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;
@@ -151,16 +153,28 @@ 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>(
@@ -368,7 +382,8 @@ 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("coffee".to_string())
+               .unwrap()
                .amount_msats(10_000_000)
                .build().unwrap();
        assert_ne!(offer.signing_pubkey(), alice_id);
@@ -393,6 +408,16 @@ 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(),
+                       amount_msats: None,
+                       features: InvoiceRequestFeatures::empty(),
+                       quantity: None,
+                       payer_note_truncated: None,
+               },
+       });
        assert_eq!(invoice_request.amount_msats(), None);
        assert_ne!(invoice_request.payer_id(), david_id);
        assert_eq!(reply_path.unwrap().introduction_node, IntroductionNode::NodeId(charlie_id));
@@ -414,7 +439,7 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
        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);
 }
 
@@ -473,7 +498,8 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() {
        }
        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,6 +510,8 @@ 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());
@@ -494,7 +522,7 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() {
        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);
 }
 
@@ -533,6 +561,16 @@ 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(),
+                       amount_msats: None,
+                       features: InvoiceRequestFeatures::empty(),
+                       quantity: None,
+                       payer_note_truncated: None,
+               },
+       });
        assert_eq!(invoice_request.amount_msats(), None);
        assert_ne!(invoice_request.payer_id(), bob_id);
        assert_eq!(reply_path.unwrap().introduction_node, IntroductionNode::NodeId(bob_id));
@@ -551,7 +589,7 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
        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);
 }
 
@@ -589,12 +627,15 @@ fn creates_and_pays_for_refund_using_one_hop_blinded_path() {
        }
        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());
@@ -605,7 +646,7 @@ fn creates_and_pays_for_refund_using_one_hop_blinded_path() {
        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);
 }
 
@@ -641,6 +682,18 @@ 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(),
+                       amount_msats: None,
+                       features: InvoiceRequestFeatures::empty(),
+                       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 +701,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);
 }
 
@@ -681,16 +734,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);
 }
 
@@ -1063,12 +1119,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 aa8ee0ce9be6bfe3a46301c4cb5d9c57610f49c5..db8c4cd033708fd36413aaf4b8c30ec05e4075e8 100644 (file)
@@ -131,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, keysend_preimage, custom_tlvs
+                       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
@@ -155,7 +156,7 @@ pub(super) fn create_recv_pending_htlc_info(
                                })?;
                        let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat };
                        (Some(payment_data), keysend_preimage, custom_tlvs,
-                        sender_intended_htlc_amt_msat, cltv_expiry_height, None,
+                        sender_intended_htlc_amt_msat, cltv_expiry_height, None, Some(payment_context),
                         intro_node_blinding_point.is_none())
                }
                msgs::InboundOnionPayload::Forward { .. } => {
@@ -241,6 +242,7 @@ pub(super) fn create_recv_pending_htlc_info(
                PendingHTLCRouting::Receive {
                        payment_data: data,
                        payment_metadata,
+                       payment_context,
                        incoming_cltv_expiry: onion_cltv_expiry,
                        phantom_shared_secret,
                        custom_tlvs,
index a75120797cafacb1dd86f07baf7afd2842844af2..d1fa58372dd93dbe7e86c2af853d6b55f150d508 100644 (file)
@@ -2142,9 +2142,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);
                                },
index c7b54a19ddcfe3585177c303a4bd3c8cab03f510..9ce230861456927f53bf4a2b65dd33e800e17e5d 100644 (file)
@@ -248,15 +248,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(dual_funding)]
+       #[cfg(splicing)]
        fn handle_splice(&self, their_node_id: &PublicKey, msg: &msgs::Splice) {
                ErroringMessageHandler::push_error(&self, their_node_id, msg.channel_id);
        }
-       #[cfg(dual_funding)]
+       #[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(dual_funding)]
+       #[cfg(splicing)]
        fn handle_splice_locked(&self, their_node_id: &PublicKey, msg: &msgs::SpliceLocked) {
                ErroringMessageHandler::push_error(&self, their_node_id, msg.channel_id);
        }
@@ -1787,16 +1787,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(dual_funding)]
+                       #[cfg(splicing)]
                        // Splicing messages:
                        wire::Message::Splice(msg) => {
                                self.message_handler.chan_handler.handle_splice(&their_node_id, &msg);
                        }
-                       #[cfg(dual_funding)]
+                       #[cfg(splicing)]
                        wire::Message::SpliceAck(msg) => {
                                self.message_handler.chan_handler.handle_splice_ack(&their_node_id, &msg);
                        }
-                       #[cfg(dual_funding)]
+                       #[cfg(splicing)]
                        wire::Message::SpliceLocked(msg) => {
                                self.message_handler.chan_handler.handle_splice_locked(&their_node_id, &msg);
                        }
index 62c82b01f59d7a36edd81cefb1cd45d407788d85..c15365629015cb3b54600efab1486532c3a888fc 100644 (file)
@@ -15,6 +15,7 @@ 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::sign::OutputSpender;
 use crate::util::test_utils;
 use crate::util::ser::Writeable;
 use crate::util::string::UntrustedString;
index 23c6b62cff074b5f76ec00dab69a683a88259255..55e31399ae10074d2e72f1b04a4c1c71ed1808df 100644 (file)
@@ -60,11 +60,11 @@ pub(crate) enum Message<T> where T: core::fmt::Debug + Type + TestEq {
        FundingCreated(msgs::FundingCreated),
        FundingSigned(msgs::FundingSigned),
        Stfu(msgs::Stfu),
-       #[cfg(dual_funding)]
+       #[cfg(splicing)]
        Splice(msgs::Splice),
-       #[cfg(dual_funding)]
+       #[cfg(splicing)]
        SpliceAck(msgs::SpliceAck),
-       #[cfg(dual_funding)]
+       #[cfg(splicing)]
        SpliceLocked(msgs::SpliceLocked),
        TxAddInput(msgs::TxAddInput),
        TxAddOutput(msgs::TxAddOutput),
@@ -118,11 +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(dual_funding)]
+                       #[cfg(splicing)]
                        &Message::Splice(ref msg) => msg.write(writer),
-                       #[cfg(dual_funding)]
+                       #[cfg(splicing)]
                        &Message::SpliceAck(ref msg) => msg.write(writer),
-                       #[cfg(dual_funding)]
+                       #[cfg(splicing)]
                        &Message::SpliceLocked(ref msg) => msg.write(writer),
                        &Message::TxAddInput(ref msg) => msg.write(writer),
                        &Message::TxAddOutput(ref msg) => msg.write(writer),
@@ -176,11 +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(dual_funding)]
+                       #[cfg(splicing)]
                        &Message::Splice(ref msg) => msg.type_id(),
-                       #[cfg(dual_funding)]
+                       #[cfg(splicing)]
                        &Message::SpliceAck(ref msg) => msg.type_id(),
-                       #[cfg(dual_funding)]
+                       #[cfg(splicing)]
                        &Message::SpliceLocked(ref msg) => msg.type_id(),
                        &Message::TxAddInput(ref msg) => msg.type_id(),
                        &Message::TxAddOutput(ref msg) => msg.type_id(),
@@ -279,18 +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(dual_funding)]
+               #[cfg(splicing)]
                msgs::Splice::TYPE => {
                        Ok(Message::Splice(Readable::read(buffer)?))
                },
                msgs::Stfu::TYPE => {
                        Ok(Message::Stfu(Readable::read(buffer)?))
                },
-               #[cfg(dual_funding)]
+               #[cfg(splicing)]
                msgs::SpliceAck::TYPE => {
                        Ok(Message::SpliceAck(Readable::read(buffer)?))
                },
-               #[cfg(dual_funding)]
+               #[cfg(splicing)]
                msgs::SpliceLocked::TYPE => {
                        Ok(Message::SpliceLocked(Readable::read(buffer)?))
                },
index fbfcffe2ad805fdec8af1f4f24ae7dc7de916e63..ee5e6deb4086bb69992015a053efdffab89c8816 100644 (file)
 
 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::schnorr::Signature;
 use bitcoin::address::{Address, Payload, WitnessProgram, WitnessVersion};
 use bitcoin::key::TweakedPublicKey;
 use core::time::Duration;
+use core::hash::{Hash, Hasher};
 use crate::io;
 use crate::blinded_path::BlindedPath;
 use crate::ln::PaymentHash;
@@ -390,6 +390,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 +404,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()),
@@ -544,7 +546,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 +616,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,
@@ -886,6 +887,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")]
@@ -1210,7 +1225,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 +1370,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)?;
 
@@ -1586,7 +1601,7 @@ mod tests {
                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();
@@ -1683,7 +1698,7 @@ mod tests {
                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!(
index 1b634525416acef1a249858a7d78cc44b3c03b06..5ae5f457a84822172d970035286a562be0d67973 100644 (file)
@@ -11,6 +11,7 @@
 
 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;
@@ -113,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 5e3ed40ac08db2b2a8a662c357bb4eb475fb45fc..9157613fcd977a132e9f7b5751f196af0592269a 100644 (file)
@@ -72,12 +72,12 @@ 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 {
@@ -529,7 +529,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 +607,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,
 
@@ -764,8 +767,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)]
@@ -868,6 +872,24 @@ 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,
+                       amount_msats: *amount_msats,
+                       features: features.clone(),
+                       quantity: *quantity,
+                       payer_note_truncated: payer_note.clone()
+                               .map(|mut s| { s.truncate(PAYER_NOTE_LIMIT); UntrustedString(s) }),
+               }
+       }
 }
 
 impl InvoiceRequestContents {
@@ -1022,7 +1044,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 +1068,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 })
@@ -1096,9 +1118,68 @@ 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 amount to pay in msats (i.e., the minimum lightning-payable unit for [`chain`]), which
+       /// must be greater than or equal to [`Offer::amount`], converted if necessary.
+       ///
+       /// [`chain`]: InvoiceRequest::chain
+       pub amount_msats: Option<u64>,
+
+       /// Features pertaining to requesting an invoice.
+       pub features: InvoiceRequestFeatures,
+
+       /// 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.amount_msats.map(|v| HighZeroBytesDroppedBigSize(v)), option),
+                       (4, WithoutLength(&self.features), required),
+                       (6, self.quantity.map(|v| HighZeroBytesDroppedBigSize(v)), option),
+                       (8, 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, amount_msats, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
+                       (4, features, (option, encoding: (InvoiceRequestFeatures, WithoutLength))),
+                       (6, quantity, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
+                       (8, payer_note_truncated, (option, encoding: (String, WithoutLength))),
+               });
+               let features = features.unwrap_or(InvoiceRequestFeatures::empty());
+
+               Ok(InvoiceRequestFields {
+                       payer_id: payer_id.0.unwrap(), amount_msats, features, 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;
@@ -1125,8 +1206,8 @@ 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() {
@@ -1192,7 +1273,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!(
@@ -1297,7 +1378,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);
 
@@ -1320,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);
 
@@ -1369,7 +1450,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);
 
@@ -1392,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);
 
@@ -2162,4 +2243,55 @@ mod tests {
                        Err(e) => assert_eq!(e, Bolt12ParseError::Decode(DecodeError::InvalidValue)),
                }
        }
+
+       #[test]
+       fn copies_verified_invoice_request_fields() {
+               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();
+
+               #[cfg(c_bindings)]
+               use crate::offers::offer::OfferWithDerivedMetadataBuilder as OfferBuilder;
+               let offer = OfferBuilder
+                       ::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
+                       .chain(Network::Testnet)
+                       .amount_msats(1000)
+                       .supported_quantity(Quantity::Unbounded)
+                       .build().unwrap();
+               assert_eq!(offer.signing_pubkey(), node_id);
+
+               let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
+                       .chain(Network::Testnet).unwrap()
+                       .amount_msats(1001).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(),
+                                               amount_msats: Some(1001),
+                                               features: InvoiceRequestFeatures::empty(),
+                                               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 da3fab589966d8d3d5988437a72aec56bbc1b8d8..a3979866926170e8b5dc467ef10c59aae9c91c54 100644 (file)
@@ -38,10 +38,20 @@ 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();
@@ -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(())
@@ -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(),
                );
        }
@@ -344,7 +360,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!(
index d390552f866b0ac1c8169482e5fa17831c57cab9..3dedc6cd35d25166263603fb58fb6173faeb8935 100644 (file)
@@ -90,11 +90,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))]
@@ -114,6 +114,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.
@@ -370,12 +401,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,
                }
        }
 } }
@@ -488,6 +522,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
@@ -577,6 +612,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()
        }
@@ -853,7 +893,7 @@ impl OfferContents {
        /// 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| {
@@ -865,9 +905,13 @@ impl OfferContents {
                                                _ => true,
                                        }
                                });
-                               signer::verify_recipient_metadata(
+                               let keys = signer::verify_recipient_metadata(
                                        metadata, key, IV_BYTES, self.signing_pubkey(), tlv_stream, secp_ctx
-                               )
+                               )?;
+
+                               let offer_id = OfferId::from_valid_invreq_tlv_stream(bytes);
+
+                               Ok((offer_id, keys))
                        },
                        None => Err(()),
                }
@@ -1002,7 +1046,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 })
        }
 }
 
@@ -1210,7 +1256,10 @@ mod tests {
                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();
@@ -1269,7 +1318,10 @@ mod tests {
                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();
index 36f6ca8c7f54436140feca10f700c29f7d02fa10..988acf7cc53d48a0dbc7d9e4ed39f572bf6381fc 100644 (file)
@@ -40,13 +40,14 @@ use bitcoin::{secp256k1, Sequence, Txid, Witness};
 use crate::chain::transaction::OutPoint;
 use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
 use crate::ln::chan_utils::{
-       make_funding_redeemscript, ChannelPublicKeys, ChannelTransactionParameters, ClosingTransaction,
-       CommitmentTransaction, HTLCOutputInCommitment, HolderCommitmentTransaction,
+       get_revokeable_redeemscript, make_funding_redeemscript, ChannelPublicKeys,
+       ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction,
+       HTLCOutputInCommitment, HolderCommitmentTransaction,
 };
 use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
 use crate::ln::channel_keys::{
-       DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, HtlcKey, RevocationBasepoint,
-       RevocationKey,
+       add_public_key_tweak, DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, HtlcKey,
+       RevocationBasepoint, RevocationKey,
 };
 #[cfg(taproot)]
 use crate::ln::msgs::PartialSignatureWithNonce;
@@ -68,6 +69,7 @@ use crate::sign::ecdsa::{EcdsaChannelSigner, WriteableEcdsaChannelSigner};
 use crate::sign::taproot::TaprootChannelSigner;
 use crate::util::atomic_counter::AtomicCounter;
 use crate::util::invoice::construct_invoice_preimage;
+use core::convert::TryInto;
 use core::ops::Deref;
 use core::sync::atomic::{AtomicUsize, Ordering};
 #[cfg(taproot)]
@@ -108,7 +110,13 @@ 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
@@ -127,6 +135,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 */ +
@@ -159,6 +168,7 @@ 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.
        ///
@@ -310,25 +320,104 @@ impl SpendableOutputDescriptor {
        ///
        /// 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::{PartiallySignedTransaction};
+       /// use bitcoin::hashes::hex::FromHex;
+       ///
+       /// # let s = "70736274ff0100520200000001dee978529ab3e61a2987bea5183713d0e6d5ceb5ac81100fdb54a1a2\
+       ///     #                69cef505000000000090000000011f26000000000000160014abb3ab63280d4ccc5c11d6b50fd427a8\
+       ///     #                e19d6470000000000001012b10270000000000002200200afe4736760d814a2651bae63b572d935d9a\
+       /// #            b74a1a16c01774e341a32afa763601054d63210394a27a700617f5b7aee72bd4f8076b5770a582b7fb\
+       ///     #                d1d4ee2ea3802cd3cfbe2067029000b27521034629b1c8fdebfaeb58a74cd181f485e2c462e594cb30\
+       ///     #                34dee655875f69f6c7c968ac20fc144c444b5f7370656e6461626c655f6f7574707574006164645f74\
+       ///     #                7765616b20a86534f38ad61dc580ef41c3886204adf0911b81619c1ad7a2f5b5de39a2ba600000";
+       /// # let psbt = PartiallySignedTransaction::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() }
                        },
-                       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.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()
                        },
                }
        }
@@ -349,8 +438,8 @@ 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>,
+       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<(PartiallySignedTransaction, u64), ()> {
@@ -442,7 +531,8 @@ impl SpendableOutputDescriptor {
                        change_destination_script,
                )?;
 
-               let psbt_inputs = descriptors.iter().map(|d| d.to_psbt_input()).collect::<Vec<_>>();
+               let psbt_inputs =
+                       descriptors.iter().map(|d| d.to_psbt_input(&secp_ctx)).collect::<Vec<_>>();
                let psbt = PartiallySignedTransaction {
                        inputs: psbt_inputs,
                        outputs: vec![Default::default(); tx.output.len()],
@@ -809,6 +899,28 @@ 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.
 #[cfg(taproot)]
@@ -886,6 +998,17 @@ pub trait SignerProvider {
        fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()>;
 }
 
+/// 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 [`WriteableEcdsaChannelSigner`] that just keeps the private keys in memory.
 ///
 /// This implementation performs no policy checks and is insufficient by itself as
@@ -1995,50 +2118,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 {
@@ -2110,6 +2189,45 @@ 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();
+
+               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)]
@@ -2242,6 +2360,25 @@ 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)]
@@ -2303,22 +2440,6 @@ 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],
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 6fd0048daf7ddeec9e3f754c0f368e44572891d4..249a089cd4883170be76bf8574855379ab48c643 100644 (file)
@@ -56,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.
@@ -70,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
@@ -212,6 +231,33 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner, K: KVStore + ?Sized> Persist<Ch
                        Err(_) => chain::ChannelMonitorUpdateStatus::UnrecoverableError
                }
        }
+
+       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,
+                       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,
+                       monitor_name.as_str(),
+                       true,
+               );
+       }
 }
 
 /// Read previously persisted [`ChannelMonitor`]s from the store.
@@ -718,6 +764,29 @@ where
                        self.persist_new_channel(funding_txo, monitor, monitor_update_call_id)
                }
        }
+
+       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>
diff --git a/lightning/src/util/sweep.rs b/lightning/src/util/sweep.rs
new file mode 100644 (file)
index 0000000..846ab96
--- /dev/null
@@ -0,0 +1,875 @@
+// 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::ChannelId;
+use crate::prelude::Vec;
+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.
+       ///
+       /// [`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>,
+       ) {
+               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;
+               }
+
+               let mut 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).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]);
+               }
+       }
+
+       /// 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 5a37e9cc13058c3814f2de2458740672c9379cfe..95bc2a7c661982ea9062497f1647af434111dcd6 100644 (file)
@@ -504,6 +504,10 @@ 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 {
@@ -552,6 +556,18 @@ impl<Signer: sign::ecdsa::WriteableEcdsaChannelSigner> chainmonitor::Persist<Sig
                }
                ret
        }
+
+       fn archive_persisted_channel(&self, funding_txo: OutPoint) { 
+               // remove the channel from the offchain_monitor_updates map
+               match self.offchain_monitor_updates.lock().unwrap().remove(&funding_txo) {
+                       Some(_) => {},
+                       None => {
+                               // If the channel was not in the offchain_monitor_updates map, it should be in the
+                               // chain_sync_monitor_persistences map.
+                               assert!(self.chain_sync_monitor_persistences.lock().unwrap().remove(&funding_txo).is_some());
+                       }
+               };
+       }
 }
 
 pub struct TestStore {
@@ -768,15 +784,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(dual_funding)]
+       #[cfg(splicing)]
        fn handle_splice(&self, _their_node_id: &PublicKey, msg: &msgs::Splice) {
                self.received_msg(wire::Message::Splice(msg.clone()));
        }
-       #[cfg(dual_funding)]
+       #[cfg(splicing)]
        fn handle_splice_ack(&self, _their_node_id: &PublicKey, msg: &msgs::SpliceAck) {
                self.received_msg(wire::Message::SpliceAck(msg.clone()));
        }
-       #[cfg(dual_funding)]
+       #[cfg(splicing)]
        fn handle_splice_locked(&self, _their_node_id: &PublicKey, msg: &msgs::SpliceLocked) {
                self.received_msg(wire::Message::SpliceLocked(msg.clone()));
        }
@@ -1366,6 +1382,10 @@ impl TestChainSource {
                        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 {