Merge pull request #2942 from benthecarman/node-id-slice
authorElias Rohrer <dev@tnull.de>
Mon, 18 Mar 2024 09:13:00 +0000 (09:13 +0000)
committerGitHub <noreply@github.com>
Mon, 18 Mar 2024 09:13:00 +0000 (09:13 +0000)
Add NodeId::from_slice

32 files changed:
ci/ci-tests.sh
fuzz/src/invoice_request_deser.rs
fuzz/src/offer_deser.rs
fuzz/src/refund_deser.rs
lightning-background-processor/src/lib.rs
lightning/src/chain/channelmonitor.rs
lightning/src/chain/mod.rs
lightning/src/crypto/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/channelmanager.rs
lightning/src/ln/functional_test_utils.rs
lightning/src/ln/functional_tests.rs
lightning/src/ln/mod.rs
lightning/src/ln/msgs.rs
lightning/src/ln/offers_tests.rs
lightning/src/ln/onion_route_tests.rs
lightning/src/ln/reload_tests.rs
lightning/src/ln/shutdown_tests.rs
lightning/src/offers/invoice.rs
lightning/src/offers/invoice_request.rs
lightning/src/offers/merkle.rs
lightning/src/offers/offer.rs
lightning/src/offers/refund.rs
lightning/src/offers/test_utils.rs
lightning/src/onion_message/messenger.rs
lightning/src/routing/gossip.rs
lightning/src/routing/router.rs
lightning/src/util/hash_tables.rs
lightning/src/util/ser.rs

index 59acb833f1f2d4fe5d7b35a03ef835bb1f682e85..5cae6d45de5f56a778fc95b2e78b5c5fd9f5ffb6 100755 (executable)
@@ -173,5 +173,7 @@ fi
 
 echo -e "\n\nTest cfg-flag builds"
 RUSTFLAGS="--cfg=taproot" cargo test --verbose --color always -p lightning
+[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
 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
index 22a2258f4e25f9df7f413bec0b1ab3d07d9a847d..414ce1cdd1cd2c17431d68ea9c1015495b9e074a 100644 (file)
@@ -9,7 +9,7 @@
 
 use bitcoin::secp256k1::{KeyPair, Parity, PublicKey, Secp256k1, SecretKey, self};
 use crate::utils::test_logger;
-use core::convert::{Infallible, TryFrom};
+use core::convert::TryFrom;
 use lightning::blinded_path::BlindedPath;
 use lightning::sign::EntropySource;
 use lightning::ln::PaymentHash;
@@ -37,16 +37,16 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
                        let even_pubkey = x_only_pubkey.public_key(Parity::Even);
                        if signing_pubkey == odd_pubkey || signing_pubkey == even_pubkey {
                                unsigned_invoice
-                                       .sign::<_, Infallible>(
-                                               |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+                                       .sign(|message: &UnsignedBolt12Invoice|
+                                               Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
                                        )
                                        .unwrap()
                                        .write(&mut buffer)
                                        .unwrap();
                        } else {
                                unsigned_invoice
-                                       .sign::<_, Infallible>(
-                                               |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+                                       .sign(|message: &UnsignedBolt12Invoice|
+                                               Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
                                        )
                                        .unwrap_err();
                        }
index e16c3b4103b4d568640ce3ff77967d02e5ca6f5b..5aad4642e461f6477a55591cf2239507882cd86a 100644 (file)
@@ -9,7 +9,7 @@
 
 use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
 use crate::utils::test_logger;
-use core::convert::{Infallible, TryFrom};
+use core::convert::TryFrom;
 use lightning::offers::invoice_request::UnsignedInvoiceRequest;
 use lightning::offers::offer::{Amount, Offer, Quantity};
 use lightning::offers::parse::Bolt12SemanticError;
@@ -29,8 +29,8 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
 
                if let Ok(invoice_request) = build_response(&offer, pubkey) {
                        invoice_request
-                               .sign::<_, Infallible>(
-                                       |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+                               .sign(|message: &UnsignedInvoiceRequest|
+                                       Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
                                )
                                .unwrap()
                                .write(&mut buffer)
index fd273d7e0284573e13899db7e8f510a0da49e0a7..14b136ec35b6d06837c09e997523fc5d093944c1 100644 (file)
@@ -9,7 +9,7 @@
 
 use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey, self};
 use crate::utils::test_logger;
-use core::convert::{Infallible, TryFrom};
+use core::convert::TryFrom;
 use lightning::blinded_path::BlindedPath;
 use lightning::sign::EntropySource;
 use lightning::ln::PaymentHash;
@@ -33,8 +33,8 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
 
                if let Ok(invoice) = build_response(&refund, pubkey, &secp_ctx) {
                        invoice
-                               .sign::<_, Infallible>(
-                                       |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+                               .sign(|message: &UnsignedBolt12Invoice|
+                                       Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
                                )
                                .unwrap()
                                .write(&mut buffer)
index 46849e136f61c5a5bcd41e38b8f39251bcaac3d6..48581766af8cb97970eeb924297315cc6c93dd2c 100644 (file)
@@ -1342,8 +1342,8 @@ mod tests {
 
        fn confirm_transaction_depth(node: &mut Node, tx: &Transaction, depth: u32) {
                for i in 1..=depth {
-                       let prev_blockhash = node.best_block.block_hash();
-                       let height = node.best_block.height() + 1;
+                       let prev_blockhash = node.best_block.block_hash;
+                       let height = node.best_block.height + 1;
                        let header = create_dummy_header(prev_blockhash, height);
                        let txdata = vec![(0, tx)];
                        node.best_block = BestBlock::new(header.block_hash(), height);
index 75271637d3c29582a8f019da2c7ce33ffb07af6f..5c81a513713f2d79d1b18a42dcc84789f9016f40 100644 (file)
@@ -387,7 +387,7 @@ impl OnchainEventEntry {
        }
 
        fn has_reached_confirmation_threshold(&self, best_block: &BestBlock) -> bool {
-               best_block.height() >= self.confirmation_threshold()
+               best_block.height >= self.confirmation_threshold()
        }
 }
 
@@ -1077,8 +1077,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signe
                        event.write(writer)?;
                }
 
-               self.best_block.block_hash().write(writer)?;
-               writer.write_all(&self.best_block.height().to_be_bytes())?;
+               self.best_block.block_hash.write(writer)?;
+               writer.write_all(&self.best_block.height.to_be_bytes())?;
 
                writer.write_all(&(self.onchain_events_awaiting_threshold_conf.len() as u64).to_be_bytes())?;
                for ref entry in self.onchain_events_awaiting_threshold_conf.iter() {
@@ -2273,7 +2273,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                                                                // before considering it "no longer pending" - this matches when we
                                                                // provide the ChannelManager an HTLC failure event.
                                                                Some(commitment_tx_output_idx) == htlc.transaction_output_index &&
-                                                                       us.best_block.height() >= event.height + ANTI_REORG_DELAY - 1
+                                                                       us.best_block.height >= event.height + ANTI_REORG_DELAY - 1
                                                        } else if let OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, .. } = event.event {
                                                                // If the HTLC was fulfilled with a preimage, we consider the HTLC
                                                                // immediately non-pending, matching when we provide ChannelManager
@@ -2674,7 +2674,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                macro_rules! claim_htlcs {
                        ($commitment_number: expr, $txid: expr) => {
                                let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None);
-                               self.onchain_tx_handler.update_claims_view_from_requests(htlc_claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
+                               self.onchain_tx_handler.update_claims_view_from_requests(htlc_claim_reqs, self.best_block.height, self.best_block.height, broadcaster, fee_estimator, logger);
                        }
                }
                if let Some(txid) = self.current_counterparty_commitment_txid {
@@ -2721,8 +2721,8 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                                // Assume that the broadcasted commitment transaction confirmed in the current best
                                // block. Even if not, its a reasonable metric for the bump criteria on the HTLC
                                // transactions.
-                               let (claim_reqs, _) = self.get_broadcasted_holder_claims(&holder_commitment_tx, self.best_block.height());
-                               self.onchain_tx_handler.update_claims_view_from_requests(claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
+                               let (claim_reqs, _) = self.get_broadcasted_holder_claims(&holder_commitment_tx, self.best_block.height);
+                               self.onchain_tx_handler.update_claims_view_from_requests(claim_reqs, self.best_block.height, self.best_block.height, broadcaster, fee_estimator, logger);
                        }
                }
        }
@@ -2736,7 +2736,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                let commitment_package = PackageTemplate::build_package(
                        self.funding_info.0.txid.clone(), self.funding_info.0.index as u32,
                        PackageSolvingData::HolderFundingOutput(funding_outp),
-                       self.best_block.height(), self.best_block.height()
+                       self.best_block.height, self.best_block.height
                );
                let mut claimable_outpoints = vec![commitment_package];
                self.pending_monitor_events.push(MonitorEvent::HolderForceClosed(self.funding_info.0));
@@ -2753,7 +2753,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                        // assuming it gets confirmed in the next block. Sadly, we have code which considers
                        // "not yet confirmed" things as discardable, so we cannot do that here.
                        let (mut new_outpoints, _) = self.get_broadcasted_holder_claims(
-                               &self.current_holder_commitment_tx, self.best_block.height()
+                               &self.current_holder_commitment_tx, self.best_block.height
                        );
                        let unsigned_commitment_tx = self.onchain_tx_handler.get_unsigned_holder_commitment_tx();
                        let new_outputs = self.get_broadcasted_holder_watch_outputs(
@@ -2777,7 +2777,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
        {
                let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs();
                self.onchain_tx_handler.update_claims_view_from_requests(
-                       claimable_outpoints, self.best_block.height(), self.best_block.height(), broadcaster,
+                       claimable_outpoints, self.best_block.height, self.best_block.height, broadcaster,
                        fee_estimator, logger
                );
        }
@@ -3593,11 +3593,11 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
        {
                let block_hash = header.block_hash();
 
-               if height > self.best_block.height() {
+               if height > self.best_block.height {
                        self.best_block = BestBlock::new(block_hash, height);
                        log_trace!(logger, "Connecting new block {} at height {}", block_hash, height);
                        self.block_confirmed(height, block_hash, vec![], vec![], vec![], &broadcaster, &fee_estimator, logger)
-               } else if block_hash != self.best_block.block_hash() {
+               } else if block_hash != self.best_block.block_hash {
                        self.best_block = BestBlock::new(block_hash, height);
                        log_trace!(logger, "Best block re-orged, replaced with new block {} at height {}", block_hash, height);
                        self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= height);
@@ -3742,7 +3742,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                        }
                }
 
-               if height > self.best_block.height() {
+               if height > self.best_block.height {
                        self.best_block = BestBlock::new(block_hash, height);
                }
 
@@ -3774,7 +3774,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                L::Target: Logger,
        {
                log_trace!(logger, "Processing {} matched transactions for block at height {}.", txn_matched.len(), conf_height);
-               debug_assert!(self.best_block.height() >= conf_height);
+               debug_assert!(self.best_block.height >= conf_height);
 
                let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
                if should_broadcast {
@@ -3865,8 +3865,8 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                        }
                }
 
-               self.onchain_tx_handler.update_claims_view_from_requests(claimable_outpoints, conf_height, self.best_block.height(), broadcaster, fee_estimator, logger);
-               self.onchain_tx_handler.update_claims_view_from_matched_txn(&txn_matched, conf_height, conf_hash, self.best_block.height(), broadcaster, fee_estimator, logger);
+               self.onchain_tx_handler.update_claims_view_from_requests(claimable_outpoints, conf_height, self.best_block.height, broadcaster, fee_estimator, logger);
+               self.onchain_tx_handler.update_claims_view_from_matched_txn(&txn_matched, conf_height, conf_hash, self.best_block.height, broadcaster, fee_estimator, logger);
 
                // Determine new outputs to watch by comparing against previously known outputs to watch,
                // updating the latter in the process.
@@ -4017,7 +4017,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
                // to the source, and if we don't fail the channel we will have to ensure that the next
                // updates that peer sends us are update_fails, failing the channel if not. It's probably
                // easier to just fail the channel as this case should be rare enough anyway.
-               let height = self.best_block.height();
+               let height = self.best_block.height;
                macro_rules! scan_commitment {
                        ($htlcs: expr, $holder_tx: expr) => {
                                for ref htlc in $htlcs {
@@ -4616,7 +4616,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
                                chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point).to_v0_p2wsh();
                }
 
-               Ok((best_block.block_hash(), ChannelMonitor::from_impl(ChannelMonitorImpl {
+               Ok((best_block.block_hash, ChannelMonitor::from_impl(ChannelMonitorImpl {
                        latest_update_id,
                        commitment_transaction_number_obscure_factor,
 
index 368dd8497b037e4821c0c41b0de7f3e7585d8042..356520b5cba86e647acdec77fd0cb86b683e7d58 100644 (file)
@@ -31,10 +31,12 @@ pub(crate) mod onchaintx;
 pub(crate) mod package;
 
 /// The best known block as identified by its hash and height.
-#[derive(Clone, Copy, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
 pub struct BestBlock {
-       block_hash: BlockHash,
-       height: u32,
+       /// The block's hash
+       pub block_hash: BlockHash,
+       /// The height at which the block was confirmed.
+       pub height: u32,
 }
 
 impl BestBlock {
@@ -51,12 +53,6 @@ impl BestBlock {
        pub fn new(block_hash: BlockHash, height: u32) -> Self {
                BestBlock { block_hash, height }
        }
-
-       /// Returns the best block hash.
-       pub fn block_hash(&self) -> BlockHash { self.block_hash }
-
-       /// Returns the best block height.
-       pub fn height(&self) -> u32 { self.height }
 }
 
 
index 6efd120667c2c980e47d53d37bc30691b1946042..b4f5af8f119f01ce908721260094ca22250361a2 100644 (file)
@@ -1,3 +1,4 @@
+#[cfg(not(fuzzing))]
 use bitcoin::hashes::cmp::fixed_time_eq;
 
 pub(crate) mod chacha20;
index d08d3c5ac6ba163b100e5392a4b86cea8fce6841..485a23e0292e520c28d47a226fe7de51abdd9690 100644 (file)
@@ -368,6 +368,10 @@ pub enum PaymentFailureReason {
        /// [`PaymentParameters::expiry_time`]: crate::routing::router::PaymentParameters::expiry_time
        PaymentExpired,
        /// We failed to find a route while retrying the payment.
+       ///
+       /// Note that this generally indicates that we've exhausted the available set of possible
+       /// routes - we tried the payment over a few routes but were not able to find any further
+       /// candidate routes beyond those.
        RouteNotFound,
        /// This error should generally never happen. This likely means that there is a problem with
        /// your router.
index 267929517d228ff36bfaca3df58e7d3fc7ddf7e0..7adac5472b543c911352bb068b0cb1f59247aebf 100644 (file)
@@ -20,7 +20,7 @@ use crate::ln::msgs;
 use crate::ln::msgs::ChannelMessageHandler;
 use crate::ln::onion_utils;
 use crate::ln::onion_utils::INVALID_ONION_BLINDING;
-use crate::ln::outbound_payment::Retry;
+use crate::ln::outbound_payment::{Retry, IDEMPOTENCY_TIMEOUT_TICKS};
 use crate::offers::invoice::BlindedPayInfo;
 use crate::prelude::*;
 use crate::routing::router::{Payee, PaymentParameters, RouteParameters};
@@ -28,11 +28,14 @@ use crate::util::config::UserConfig;
 use crate::util::test_utils;
 
 fn blinded_payment_path(
-       payment_secret: PaymentSecret, node_ids: Vec<PublicKey>,
-       channel_upds: &[&msgs::UnsignedChannelUpdate], keys_manager: &test_utils::TestKeysInterface
+       payment_secret: PaymentSecret, intro_node_min_htlc: u64, intro_node_max_htlc: u64,
+       node_ids: Vec<PublicKey>, channel_upds: &[&msgs::UnsignedChannelUpdate],
+       keys_manager: &test_utils::TestKeysInterface
 ) -> (BlindedPayInfo, BlindedPath) {
        let mut intermediate_nodes = Vec::new();
-       for (node_id, chan_upd) in node_ids.iter().zip(channel_upds) {
+       let mut intro_node_min_htlc_opt = Some(intro_node_min_htlc);
+       let mut intro_node_max_htlc_opt = Some(intro_node_max_htlc);
+       for (idx, (node_id, chan_upd)) in node_ids.iter().zip(channel_upds).enumerate() {
                intermediate_nodes.push(ForwardNode {
                        node_id: *node_id,
                        tlvs: ForwardTlvs {
@@ -44,34 +47,42 @@ fn blinded_payment_path(
                                },
                                payment_constraints: PaymentConstraints {
                                        max_cltv_expiry: u32::max_value(),
-                                       htlc_minimum_msat: chan_upd.htlc_minimum_msat,
+                                       htlc_minimum_msat: intro_node_min_htlc_opt.take()
+                                               .unwrap_or_else(|| channel_upds[idx - 1].htlc_minimum_msat),
                                },
                                features: BlindedHopFeatures::empty(),
                        },
-                       htlc_maximum_msat: chan_upd.htlc_maximum_msat,
+                       htlc_maximum_msat: intro_node_max_htlc_opt.take()
+                               .unwrap_or_else(|| channel_upds[idx - 1].htlc_maximum_msat),
                });
        }
        let payee_tlvs = ReceiveTlvs {
                payment_secret,
                payment_constraints: PaymentConstraints {
                        max_cltv_expiry: u32::max_value(),
-                       htlc_minimum_msat: channel_upds.last().unwrap().htlc_minimum_msat,
+                       htlc_minimum_msat:
+                               intro_node_min_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_minimum_msat),
                },
        };
        let mut secp_ctx = Secp256k1::new();
        BlindedPath::new_for_payment(
                &intermediate_nodes[..], *node_ids.last().unwrap(), payee_tlvs,
-               channel_upds.last().unwrap().htlc_maximum_msat, TEST_FINAL_CLTV as u16, keys_manager, &secp_ctx
+               intro_node_max_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_maximum_msat),
+               TEST_FINAL_CLTV as u16, keys_manager, &secp_ctx
        ).unwrap()
 }
 
 pub fn get_blinded_route_parameters(
-       amt_msat: u64, payment_secret: PaymentSecret, node_ids: Vec<PublicKey>,
-       channel_upds: &[&msgs::UnsignedChannelUpdate], keys_manager: &test_utils::TestKeysInterface
+       amt_msat: u64, payment_secret: PaymentSecret, intro_node_min_htlc: u64, intro_node_max_htlc: u64,
+       node_ids: Vec<PublicKey>, channel_upds: &[&msgs::UnsignedChannelUpdate],
+       keys_manager: &test_utils::TestKeysInterface
 ) -> RouteParameters {
        RouteParameters::from_payment_params_and_value(
                PaymentParameters::blinded(vec![
-                       blinded_payment_path(payment_secret, node_ids, channel_upds, keys_manager)
+                       blinded_payment_path(
+                               payment_secret, intro_node_min_htlc, intro_node_max_htlc, node_ids, channel_upds,
+                               keys_manager
+                       )
                ]), amt_msat
        )
 }
@@ -169,6 +180,70 @@ fn mpp_to_one_hop_blinded_path() {
        claim_payment_along_route(&nodes[0], expected_route, false, payment_preimage);
 }
 
+#[test]
+fn mpp_to_three_hop_blinded_paths() {
+       let chanmon_cfgs = create_chanmon_cfgs(6);
+       let node_cfgs = create_node_cfgs(6, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(6, &node_cfgs, &[None, None, None, None, None, None]);
+       let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
+
+       // Create this network topology so node 0 MPP's over 2 3-hop blinded paths:
+       //     n1 -- n3
+       //    /        \
+       // n0           n5
+       //    \        /
+       //     n2 -- n4
+       create_announced_chan_between_nodes(&nodes, 0, 1);
+       create_announced_chan_between_nodes(&nodes, 0, 2);
+       let chan_upd_1_3 = create_announced_chan_between_nodes(&nodes, 1, 3).0.contents;
+       let chan_upd_2_4 = create_announced_chan_between_nodes(&nodes, 2, 4).0.contents;
+       let chan_upd_3_5 = create_announced_chan_between_nodes(&nodes, 3, 5).0.contents;
+       let chan_upd_4_5 = create_announced_chan_between_nodes(&nodes, 4, 5).0.contents;
+
+       let amt_msat = 15_000_000;
+       let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[5], Some(amt_msat), None);
+       let route_params = {
+               let path_1_params = get_blinded_route_parameters(
+                       amt_msat, payment_secret, 1, 1_0000_0000, vec![
+                               nodes[1].node.get_our_node_id(), nodes[3].node.get_our_node_id(),
+                               nodes[5].node.get_our_node_id()
+                       ], &[&chan_upd_1_3, &chan_upd_3_5], &chanmon_cfgs[5].keys_manager
+               );
+               let path_2_params = get_blinded_route_parameters(
+                       amt_msat, payment_secret, 1, 1_0000_0000, vec![
+                               nodes[2].node.get_our_node_id(), nodes[4].node.get_our_node_id(),
+                               nodes[5].node.get_our_node_id()
+                       ], &[&chan_upd_2_4, &chan_upd_4_5], &chanmon_cfgs[5].keys_manager
+               );
+               let pay_params = PaymentParameters::blinded(
+                       vec![
+                               path_1_params.payment_params.payee.blinded_route_hints()[0].clone(),
+                               path_2_params.payment_params.payee.blinded_route_hints()[0].clone()
+                       ]
+               )
+                       .with_bolt12_features(channelmanager::provided_bolt12_invoice_features(&UserConfig::default()))
+                       .unwrap();
+               RouteParameters::from_payment_params_and_value(pay_params, amt_msat)
+       };
+
+       nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(),
+               PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
+       check_added_monitors(&nodes[0], 2);
+
+       let expected_route: &[&[&Node]] = &[&[&nodes[1], &nodes[3], &nodes[5]], &[&nodes[2], &nodes[4], &nodes[5]]];
+       let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+       assert_eq!(events.len(), 2);
+
+       let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
+       pass_along_path(&nodes[0], expected_route[0], amt_msat, payment_hash.clone(),
+               Some(payment_secret), ev.clone(), false, None);
+
+       let ev = remove_first_msg_event_to_node(&nodes[2].node.get_our_node_id(), &mut events);
+       pass_along_path(&nodes[0], expected_route[1], amt_msat, payment_hash.clone(),
+               Some(payment_secret), ev.clone(), true, None);
+       claim_payment_along_route(&nodes[0], expected_route, false, payment_preimage);
+}
+
 enum ForwardCheckFail {
        // Fail a check on the inbound onion payload. In this case, we underflow when calculating the
        // outgoing cltv_expiry.
@@ -181,63 +256,108 @@ enum ForwardCheckFail {
 
 #[test]
 fn forward_checks_failure() {
-       do_forward_checks_failure(ForwardCheckFail::InboundOnionCheck);
-       do_forward_checks_failure(ForwardCheckFail::ForwardPayloadEncodedAsReceive);
-       do_forward_checks_failure(ForwardCheckFail::OutboundChannelCheck);
+       do_forward_checks_failure(ForwardCheckFail::InboundOnionCheck, true);
+       do_forward_checks_failure(ForwardCheckFail::InboundOnionCheck, false);
+       do_forward_checks_failure(ForwardCheckFail::ForwardPayloadEncodedAsReceive, true);
+       do_forward_checks_failure(ForwardCheckFail::ForwardPayloadEncodedAsReceive, false);
+       do_forward_checks_failure(ForwardCheckFail::OutboundChannelCheck, true);
+       do_forward_checks_failure(ForwardCheckFail::OutboundChannelCheck, false);
 }
 
-fn do_forward_checks_failure(check: ForwardCheckFail) {
+fn do_forward_checks_failure(check: ForwardCheckFail, intro_fails: bool) {
        // Ensure we'll fail backwards properly if a forwarding check fails on initial update_add
        // receipt.
-       let chanmon_cfgs = create_chanmon_cfgs(3);
-       let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
-       let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
-       let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+       let chanmon_cfgs = create_chanmon_cfgs(4);
+       let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
+       let mut nodes = create_network(4, &node_cfgs, &node_chanmgrs);
        // We need the session priv to construct a bogus onion packet later.
        *nodes[0].keys_manager.override_random_bytes.lock().unwrap() = Some([3; 32]);
        create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
        let chan_upd_1_2 = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0).0.contents;
+       let chan_upd_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0).0.contents;
 
        let amt_msat = 5000;
-       let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), None);
-       let route_params = get_blinded_route_parameters(amt_msat, payment_secret,
-               nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2],
-               &chanmon_cfgs[2].keys_manager);
+       let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[3], Some(amt_msat), None);
+       let route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
+               nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(),
+               &[&chan_upd_1_2, &chan_upd_2_3], &chanmon_cfgs[3].keys_manager);
 
        let route = get_route(&nodes[0], &route_params).unwrap();
        node_cfgs[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
        nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
        check_added_monitors(&nodes[0], 1);
 
-       let mut events = nodes[0].node.get_and_clear_pending_msg_events();
-       assert_eq!(events.len(), 1);
-       let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
-       let mut payment_event = SendEvent::from_event(ev);
+       macro_rules! cause_error {
+               ($src_node_idx: expr, $target_node_idx: expr, $update_add: expr) => {
+                       match check {
+                               ForwardCheckFail::InboundOnionCheck => {
+                                       $update_add.cltv_expiry = 10; // causes outbound CLTV expiry to underflow
+                               },
+                               ForwardCheckFail::ForwardPayloadEncodedAsReceive => {
+                                       let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
+                                       let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
+                                       let cur_height = nodes[0].best_block_info().1;
+                                       let (mut onion_payloads, ..) = onion_utils::build_onion_payloads(
+                                               &route.paths[0], amt_msat, RecipientOnionFields::spontaneous_empty(), cur_height, &None).unwrap();
+                                       // Remove the receive payload so the blinded forward payload is encoded as a final payload
+                                       // (i.e. next_hop_hmac == [0; 32])
+                                       onion_payloads.pop();
+                                       if $target_node_idx + 1 < nodes.len() {
+                                               onion_payloads.pop();
+                                       }
+                                       $update_add.onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
+                               },
+                               ForwardCheckFail::OutboundChannelCheck => {
+                                       // The intro node will see that the next-hop peer is disconnected and fail the HTLC backwards.
+                                       nodes[$src_node_idx].node.peer_disconnected(&nodes[$target_node_idx].node.get_our_node_id());
+                               }
+                       }
+               }
+       }
 
-       let mut update_add = &mut payment_event.msgs[0];
-       match check {
-               ForwardCheckFail::InboundOnionCheck => {
-                       update_add.cltv_expiry = 10; // causes outbound CLTV expiry to underflow
-               },
-               ForwardCheckFail::ForwardPayloadEncodedAsReceive => {
-                       let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
-                       let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
-                       let cur_height = nodes[0].best_block_info().1;
-                       let (mut onion_payloads, ..) = onion_utils::build_onion_payloads(
-                               &route.paths[0], amt_msat, RecipientOnionFields::spontaneous_empty(), cur_height, &None).unwrap();
-                       // Remove the receive payload so the blinded forward payload is encoded as a final payload
-                       // (i.e. next_hop_hmac == [0; 32])
-                       onion_payloads.pop();
-                       update_add.onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
-               },
-               ForwardCheckFail::OutboundChannelCheck => {
-                       // The intro node will see that the next-hop peer is disconnected and fail the HTLC backwards.
-                       nodes[1].node.peer_disconnected(&nodes[2].node.get_our_node_id());
-               },
+       let mut updates_0_1 = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+       let update_add = &mut updates_0_1.update_add_htlcs[0];
+
+       if intro_fails {
+               cause_error!(1, 2, update_add);
        }
-       nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
+
+       nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &update_add);
        check_added_monitors!(nodes[1], 0);
-       do_commitment_signed_dance(&nodes[1], &nodes[0], &payment_event.commitment_msg, true, true);
+       do_commitment_signed_dance(&nodes[1], &nodes[0], &updates_0_1.commitment_signed, true, true);
+
+       if intro_fails {
+               let mut updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+               nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &updates.update_fail_htlcs[0]);
+               do_commitment_signed_dance(&nodes[0], &nodes[1], &updates.commitment_signed, false, false);
+               expect_payment_failed_conditions(&nodes[0], payment_hash, false,
+                       PaymentFailedConditions::new().expected_htlc_error_data(INVALID_ONION_BLINDING, &[0; 32]));
+               return
+       }
+
+       expect_pending_htlcs_forwardable!(nodes[1]);
+       check_added_monitors!(nodes[1], 1);
+
+       let mut updates_1_2 = get_htlc_update_msgs!(nodes[1], nodes[2].node.get_our_node_id());
+       let mut update_add = &mut updates_1_2.update_add_htlcs[0];
+
+       cause_error!(2, 3, update_add);
+
+       nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add);
+       check_added_monitors!(nodes[2], 0);
+       do_commitment_signed_dance(&nodes[2], &nodes[1], &updates_1_2.commitment_signed, true, true);
+
+       let mut updates = get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
+       let update_malformed = &mut updates.update_fail_malformed_htlcs[0];
+       assert_eq!(update_malformed.failure_code, INVALID_ONION_BLINDING);
+       assert_eq!(update_malformed.sha256_of_onion, [0; 32]);
+
+       // Ensure the intro node will properly blind the error if its downstream node failed to do so.
+       update_malformed.sha256_of_onion = [1; 32];
+       update_malformed.failure_code = INVALID_ONION_BLINDING ^ 1;
+       nodes[1].node.handle_update_fail_malformed_htlc(&nodes[2].node.get_our_node_id(), update_malformed);
+       do_commitment_signed_dance(&nodes[1], &nodes[2], &updates.commitment_signed, true, false);
 
        let mut updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
        nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &updates.update_fail_htlcs[0]);
@@ -259,7 +379,7 @@ fn failed_backwards_to_intro_node() {
 
        let amt_msat = 5000;
        let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), None);
-       let route_params = get_blinded_route_parameters(amt_msat, payment_secret,
+       let route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
                nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2],
                &chanmon_cfgs[2].keys_manager);
 
@@ -315,26 +435,32 @@ enum ProcessPendingHTLCsCheck {
 
 #[test]
 fn forward_fail_in_process_pending_htlc_fwds() {
-       do_forward_fail_in_process_pending_htlc_fwds(ProcessPendingHTLCsCheck::FwdPeerDisconnected);
-       do_forward_fail_in_process_pending_htlc_fwds(ProcessPendingHTLCsCheck::FwdChannelClosed);
+       do_forward_fail_in_process_pending_htlc_fwds(ProcessPendingHTLCsCheck::FwdPeerDisconnected, true);
+       do_forward_fail_in_process_pending_htlc_fwds(ProcessPendingHTLCsCheck::FwdPeerDisconnected, false);
+       do_forward_fail_in_process_pending_htlc_fwds(ProcessPendingHTLCsCheck::FwdChannelClosed, true);
+       do_forward_fail_in_process_pending_htlc_fwds(ProcessPendingHTLCsCheck::FwdChannelClosed, false);
 }
-fn do_forward_fail_in_process_pending_htlc_fwds(check: ProcessPendingHTLCsCheck) {
+fn do_forward_fail_in_process_pending_htlc_fwds(check: ProcessPendingHTLCsCheck, intro_fails: bool) {
        // Ensure the intro node will error backwards properly if the HTLC fails in
        // process_pending_htlc_forwards.
-       let chanmon_cfgs = create_chanmon_cfgs(3);
-       let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
-       let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
-       let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+       let chanmon_cfgs = create_chanmon_cfgs(4);
+       let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
+       let mut nodes = create_network(4, &node_cfgs, &node_chanmgrs);
        create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
-       let (chan_upd_1_2, channel_id) = {
+       let (chan_upd_1_2, chan_id_1_2) = {
                let chan = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
                (chan.0.contents, chan.2)
        };
+       let (chan_upd_2_3, chan_id_2_3) = {
+               let chan = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0);
+               (chan.0.contents, chan.2)
+       };
 
        let amt_msat = 5000;
        let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), None);
-       let route_params = get_blinded_route_parameters(amt_msat, payment_secret,
-               nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2],
+       let route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
+               nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2, &chan_upd_2_3],
                &chanmon_cfgs[2].keys_manager);
 
        nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
@@ -349,43 +475,80 @@ fn do_forward_fail_in_process_pending_htlc_fwds(check: ProcessPendingHTLCsCheck)
        check_added_monitors!(nodes[1], 0);
        do_commitment_signed_dance(&nodes[1], &nodes[0], &payment_event.commitment_msg, false, false);
 
-       match check {
-               ProcessPendingHTLCsCheck::FwdPeerDisconnected => {
-                       // Disconnect the next-hop peer so when we go to forward in process_pending_htlc_forwards, the
-                       // intro node will error backwards.
-                       nodes[1].node.peer_disconnected(&nodes[2].node.get_our_node_id());
-                       expect_pending_htlcs_forwardable!(nodes[1]);
-                       expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!(nodes[1],
-                               vec![HTLCDestination::NextHopChannel { node_id: Some(nodes[2].node.get_our_node_id()), channel_id }]);
-               },
-               ProcessPendingHTLCsCheck::FwdChannelClosed => {
-                       // Force close the next-hop channel so when we go to forward in process_pending_htlc_forwards,
-                       // the intro node will error backwards.
-                       nodes[1].node.force_close_broadcasting_latest_txn(&channel_id, &nodes[2].node.get_our_node_id()).unwrap();
-                       let events = nodes[1].node.get_and_clear_pending_events();
-                       match events[0] {
-                               crate::events::Event::PendingHTLCsForwardable { .. } => {},
-                               _ => panic!("Unexpected event {:?}", events),
-                       };
-                       match events[1] {
-                               crate::events::Event::ChannelClosed { .. } => {},
-                               _ => panic!("Unexpected event {:?}", events),
+       macro_rules! cause_error {
+               ($prev_node: expr, $curr_node: expr, $next_node: expr, $failed_chan_id: expr, $failed_scid: expr) => {
+                       match check {
+                               ProcessPendingHTLCsCheck::FwdPeerDisconnected => {
+                                       // Disconnect the next-hop peer so when we go to forward in process_pending_htlc_forwards, the
+                                       // intro node will error backwards.
+                                       $curr_node.node.peer_disconnected(&$next_node.node.get_our_node_id());
+                                       expect_pending_htlcs_forwardable!($curr_node);
+                                       expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!($curr_node,
+                                               vec![HTLCDestination::NextHopChannel { node_id: Some($next_node.node.get_our_node_id()), channel_id: $failed_chan_id }]);
+                               },
+                               ProcessPendingHTLCsCheck::FwdChannelClosed => {
+                                       // Force close the next-hop channel so when we go to forward in process_pending_htlc_forwards,
+                                       // the intro node will error backwards.
+                                       $curr_node.node.force_close_broadcasting_latest_txn(&$failed_chan_id, &$next_node.node.get_our_node_id()).unwrap();
+                                       let events = $curr_node.node.get_and_clear_pending_events();
+                                       match events[0] {
+                                               crate::events::Event::PendingHTLCsForwardable { .. } => {},
+                                               _ => panic!("Unexpected event {:?}", events),
+                                       };
+                                       match events[1] {
+                                               crate::events::Event::ChannelClosed { .. } => {},
+                                               _ => panic!("Unexpected event {:?}", events),
+                                       }
+
+                                       $curr_node.node.process_pending_htlc_forwards();
+                                       expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!($curr_node,
+                                               vec![HTLCDestination::UnknownNextHop { requested_forward_scid: $failed_scid }]);
+                                       check_closed_broadcast(&$curr_node, 1, true);
+                                       check_added_monitors!($curr_node, 1);
+                                       $curr_node.node.process_pending_htlc_forwards();
+                               },
                        }
+               }
+       }
 
-                       nodes[1].node.process_pending_htlc_forwards();
-                       expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!(nodes[1],
-                               vec![HTLCDestination::UnknownNextHop { requested_forward_scid: chan_upd_1_2.short_channel_id }]);
-                       check_closed_broadcast(&nodes[1], 1, true);
-                       check_added_monitors!(nodes[1], 1);
-                       nodes[1].node.process_pending_htlc_forwards();
-               },
+       if intro_fails {
+               cause_error!(nodes[0], nodes[1], nodes[2], chan_id_1_2, chan_upd_1_2.short_channel_id);
+               let mut updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+               nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &updates.update_fail_htlcs[0]);
+               check_added_monitors!(nodes[1], 1);
+               do_commitment_signed_dance(&nodes[0], &nodes[1], &updates.commitment_signed, false, false);
+
+               expect_payment_failed_conditions(&nodes[0], payment_hash, false,
+                       PaymentFailedConditions::new().expected_htlc_error_data(INVALID_ONION_BLINDING, &[0; 32]));
+               return
        }
 
+       expect_pending_htlcs_forwardable!(nodes[1]);
+       check_added_monitors!(nodes[1], 1);
+
+       let mut updates_1_2 = get_htlc_update_msgs!(nodes[1], nodes[2].node.get_our_node_id());
+       let mut update_add = &mut updates_1_2.update_add_htlcs[0];
+       nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add);
+       check_added_monitors!(nodes[2], 0);
+       do_commitment_signed_dance(&nodes[2], &nodes[1], &updates_1_2.commitment_signed, true, true);
+
+       cause_error!(nodes[1], nodes[2], nodes[3], chan_id_2_3, chan_upd_2_3.short_channel_id);
+       check_added_monitors!(nodes[2], 1);
+
+       let mut updates = get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
+       let update_malformed = &mut updates.update_fail_malformed_htlcs[0];
+       assert_eq!(update_malformed.failure_code, INVALID_ONION_BLINDING);
+       assert_eq!(update_malformed.sha256_of_onion, [0; 32]);
+
+       // Ensure the intro node will properly blind the error if its downstream node failed to do so.
+       update_malformed.sha256_of_onion = [1; 32];
+       update_malformed.failure_code = INVALID_ONION_BLINDING ^ 1;
+       nodes[1].node.handle_update_fail_malformed_htlc(&nodes[2].node.get_our_node_id(), update_malformed);
+       do_commitment_signed_dance(&nodes[1], &nodes[2], &updates.commitment_signed, true, false);
+
        let mut updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
        nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &updates.update_fail_htlcs[0]);
-       check_added_monitors!(nodes[1], 1);
        do_commitment_signed_dance(&nodes[0], &nodes[1], &updates.commitment_signed, false, false);
-
        expect_payment_failed_conditions(&nodes[0], payment_hash, false,
                PaymentFailedConditions::new().expected_htlc_error_data(INVALID_ONION_BLINDING, &[0; 32]));
 }
@@ -413,7 +576,7 @@ fn do_blinded_intercept_payment(intercept_node_fails: bool) {
        let intercept_scid = nodes[1].node.get_intercept_scid();
        let mut intercept_chan_upd = chan_upd;
        intercept_chan_upd.short_channel_id = intercept_scid;
-       let route_params = get_blinded_route_parameters(amt_msat, payment_secret,
+       let route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
                nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&intercept_chan_upd],
                &chanmon_cfgs[2].keys_manager);
 
@@ -490,7 +653,7 @@ fn two_hop_blinded_path_success() {
 
        let amt_msat = 5000;
        let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), None);
-       let route_params = get_blinded_route_parameters(amt_msat, payment_secret,
+       let route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
                nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2],
                &chanmon_cfgs[2].keys_manager);
 
@@ -519,7 +682,7 @@ fn three_hop_blinded_path_success() {
 
        let amt_msat = 5000;
        let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[4], Some(amt_msat), None);
-       let route_params = get_blinded_route_parameters(amt_msat, payment_secret,
+       let route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
                nodes.iter().skip(2).map(|n| n.node.get_our_node_id()).collect(),
                &[&chan_upd_2_3, &chan_upd_3_4], &chanmon_cfgs[4].keys_manager);
 
@@ -529,6 +692,59 @@ fn three_hop_blinded_path_success() {
        claim_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3], &nodes[4]], payment_preimage);
 }
 
+#[test]
+fn three_hop_blinded_path_fail() {
+       // Test that an intermediate blinded forwarding node gets failed back to with
+       // malformed and also fails back themselves with malformed.
+       let chanmon_cfgs = create_chanmon_cfgs(4);
+       let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
+       let mut nodes = create_network(4, &node_cfgs, &node_chanmgrs);
+       create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
+       let chan_upd_1_2 = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0).0.contents;
+       let chan_upd_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0).0.contents;
+
+       let amt_msat = 5000;
+       let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[3], Some(amt_msat), None);
+       let route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
+               nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(),
+               &[&chan_upd_1_2, &chan_upd_2_3], &chanmon_cfgs[3].keys_manager);
+
+       nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
+       check_added_monitors(&nodes[0], 1);
+       pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3]]], amt_msat, payment_hash, payment_secret);
+
+       nodes[3].node.fail_htlc_backwards(&payment_hash);
+       expect_pending_htlcs_forwardable_conditions(
+               nodes[3].node.get_and_clear_pending_events(), &[HTLCDestination::FailedPayment { payment_hash }]
+       );
+       nodes[3].node.process_pending_htlc_forwards();
+       check_added_monitors!(nodes[3], 1);
+
+       let updates_3_2 = get_htlc_update_msgs!(nodes[3], nodes[2].node.get_our_node_id());
+       assert_eq!(updates_3_2.update_fail_malformed_htlcs.len(), 1);
+       let update_malformed = &updates_3_2.update_fail_malformed_htlcs[0];
+       assert_eq!(update_malformed.sha256_of_onion, [0; 32]);
+       assert_eq!(update_malformed.failure_code, INVALID_ONION_BLINDING);
+       nodes[2].node.handle_update_fail_malformed_htlc(&nodes[3].node.get_our_node_id(), update_malformed);
+       do_commitment_signed_dance(&nodes[2], &nodes[3], &updates_3_2.commitment_signed, true, false);
+
+       let updates_2_1 = get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
+       assert_eq!(updates_2_1.update_fail_malformed_htlcs.len(), 1);
+       let update_malformed = &updates_2_1.update_fail_malformed_htlcs[0];
+       assert_eq!(update_malformed.sha256_of_onion, [0; 32]);
+       assert_eq!(update_malformed.failure_code, INVALID_ONION_BLINDING);
+       nodes[1].node.handle_update_fail_malformed_htlc(&nodes[2].node.get_our_node_id(), update_malformed);
+       do_commitment_signed_dance(&nodes[1], &nodes[2], &updates_2_1.commitment_signed, true, false);
+
+       let updates_1_0 = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+       assert_eq!(updates_1_0.update_fail_htlcs.len(), 1);
+       nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &updates_1_0.update_fail_htlcs[0]);
+       do_commitment_signed_dance(&nodes[0], &nodes[1], &updates_1_0.commitment_signed, false, false);
+       expect_payment_failed_conditions(&nodes[0], payment_hash, false,
+               PaymentFailedConditions::new().expected_htlc_error_data(INVALID_ONION_BLINDING, &[0; 32]));
+}
+
 #[derive(PartialEq)]
 enum ReceiveCheckFail {
        // The recipient fails the payment upon `PaymentClaimable`.
@@ -581,7 +797,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
                Some(TEST_FINAL_CLTV as u16 - 2)
        } else { None };
        let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), excess_final_cltv_delta_opt);
-       let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret,
+       let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
                nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2],
                &chanmon_cfgs[2].keys_manager);
 
@@ -598,7 +814,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
                let high_htlc_min_bp = {
                        let mut high_htlc_minimum_upd = chan_upd_1_2.clone();
                        high_htlc_minimum_upd.htlc_minimum_msat = amt_msat + 1000;
-                       let high_htlc_min_params = get_blinded_route_parameters(amt_msat, payment_secret,
+                       let high_htlc_min_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000,
                                nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&high_htlc_minimum_upd],
                                &chanmon_cfgs[2].keys_manager);
                        if let Payee::Blinded { route_hints, .. } = high_htlc_min_params.payment_params.payee {
@@ -768,11 +984,11 @@ fn blinded_path_retries() {
        let route_params = {
                let pay_params = PaymentParameters::blinded(
                        vec![
-                               blinded_payment_path(payment_secret,
+                               blinded_payment_path(payment_secret, 1, 1_0000_0000,
                                        vec![nodes[1].node.get_our_node_id(), nodes[3].node.get_our_node_id()], &[&chan_1_3.0.contents],
                                        &chanmon_cfgs[3].keys_manager
                                ),
-                               blinded_payment_path(payment_secret,
+                               blinded_payment_path(payment_secret, 1, 1_0000_0000,
                                        vec![nodes[2].node.get_our_node_id(), nodes[3].node.get_our_node_id()], &[&chan_2_3.0.contents],
                                        &chanmon_cfgs[3].keys_manager
                                ),
@@ -846,3 +1062,124 @@ fn blinded_path_retries() {
                _ => panic!()
        }
 }
+
+#[test]
+fn min_htlc() {
+       // The min htlc of a blinded path is the max (htlc_min - following_fees) along the path. Make sure
+       // the payment succeeds when we calculate the min htlc this way.
+       let chanmon_cfgs = create_chanmon_cfgs(4);
+       let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
+       let mut node_1_cfg = test_default_channel_config();
+       node_1_cfg.channel_handshake_config.our_htlc_minimum_msat = 2000;
+       node_1_cfg.channel_config.forwarding_fee_base_msat = 1000;
+       node_1_cfg.channel_config.forwarding_fee_proportional_millionths = 100_000;
+       let mut node_2_cfg = test_default_channel_config();
+       node_2_cfg.channel_handshake_config.our_htlc_minimum_msat = 5000;
+       node_2_cfg.channel_config.forwarding_fee_base_msat = 200;
+       node_2_cfg.channel_config.forwarding_fee_proportional_millionths = 150_000;
+       let mut node_3_cfg = test_default_channel_config();
+       node_3_cfg.channel_handshake_config.our_htlc_minimum_msat = 2000;
+       let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, Some(node_1_cfg), Some(node_2_cfg), Some(node_3_cfg)]);
+       let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
+       create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
+       let chan_1_2 = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
+       let chan_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0);
+
+       let min_htlc_msat = {
+               // The min htlc for this setup is nodes[2]'s htlc_minimum_msat minus the
+               // following fees.
+               let post_base_fee = chan_2_3.1.contents.htlc_minimum_msat - chan_2_3.0.contents.fee_base_msat as u64;
+               let prop_fee = chan_2_3.0.contents.fee_proportional_millionths as u64;
+               (post_base_fee * 1_000_000 + 1_000_000 + prop_fee - 1) / (prop_fee + 1_000_000)
+       };
+       let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[3], Some(min_htlc_msat), None);
+       let mut route_params = get_blinded_route_parameters(
+               min_htlc_msat, payment_secret, chan_1_2.1.contents.htlc_minimum_msat,
+               chan_1_2.1.contents.htlc_maximum_msat, vec![nodes[1].node.get_our_node_id(),
+               nodes[2].node.get_our_node_id(), nodes[3].node.get_our_node_id()],
+               &[&chan_1_2.0.contents, &chan_2_3.0.contents], &chanmon_cfgs[3].keys_manager);
+       assert_eq!(min_htlc_msat,
+               route_params.payment_params.payee.blinded_route_hints()[0].0.htlc_minimum_msat);
+
+       nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap();
+       check_added_monitors(&nodes[0], 1);
+       pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3]]], min_htlc_msat, payment_hash, payment_secret);
+       claim_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3]], payment_preimage);
+
+       // Paying 1 less than the min fails.
+       for _ in 0..IDEMPOTENCY_TIMEOUT_TICKS + 1 {
+               nodes[0].node.timer_tick_occurred();
+       }
+       if let Payee::Blinded { ref mut route_hints, .. } = route_params.payment_params.payee {
+               route_hints[0].0.htlc_minimum_msat -= 1;
+       } else { panic!() }
+       route_params.final_value_msat -= 1;
+       nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
+       check_added_monitors(&nodes[0], 1);
+
+       let mut payment_event_0_1 = {
+               let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+               assert_eq!(events.len(), 1);
+               let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
+               SendEvent::from_event(ev)
+       };
+       nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event_0_1.msgs[0]);
+       check_added_monitors!(nodes[1], 0);
+       do_commitment_signed_dance(&nodes[1], &nodes[0], &payment_event_0_1.commitment_msg, true, true);
+       let mut updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+       nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &updates.update_fail_htlcs[0]);
+       do_commitment_signed_dance(&nodes[0], &nodes[1], &updates.commitment_signed, false, false);
+       expect_payment_failed_conditions(&nodes[0], payment_hash, false,
+               PaymentFailedConditions::new().expected_htlc_error_data(INVALID_ONION_BLINDING, &[0; 32]));
+}
+
+#[test]
+fn conditionally_round_fwd_amt() {
+       // Previously, the (rng-found) feerates below caught a bug where an intermediate node would
+       // calculate an amt_to_forward that underpaid them by 1 msat, caused by rounding up the outbound
+       // amount on top of an already rounded-up total routing fee. Ensure that we'll conditionally round
+       // down intermediate nodes' outbound amounts based on whether rounding up will result in
+       // undercharging for relay.
+       let chanmon_cfgs = create_chanmon_cfgs(5);
+       let node_cfgs = create_node_cfgs(5, &chanmon_cfgs);
+
+       let mut node_1_cfg = test_default_channel_config();
+       node_1_cfg.channel_config.forwarding_fee_base_msat = 247371;
+       node_1_cfg.channel_config.forwarding_fee_proportional_millionths = 86552;
+
+       let mut node_2_cfg = test_default_channel_config();
+       node_2_cfg.channel_config.forwarding_fee_base_msat = 198921;
+       node_2_cfg.channel_config.forwarding_fee_proportional_millionths = 681759;
+
+       let mut node_3_cfg = test_default_channel_config();
+       node_3_cfg.channel_config.forwarding_fee_base_msat = 132845;
+       node_3_cfg.channel_config.forwarding_fee_proportional_millionths = 552561;
+
+       let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None, Some(node_1_cfg), Some(node_2_cfg), Some(node_3_cfg), None]);
+       let nodes = create_network(5, &node_cfgs, &node_chanmgrs);
+       create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
+       let chan_1_2 = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
+       let chan_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0);
+       let chan_3_4 = create_announced_chan_between_nodes_with_value(&nodes, 3, 4, 1_000_000, 0);
+
+       let amt_msat = 100_000;
+       let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[4], Some(amt_msat), None);
+       let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret,
+               chan_1_2.1.contents.htlc_minimum_msat, chan_1_2.1.contents.htlc_maximum_msat,
+               vec![nodes[1].node.get_our_node_id(), nodes[2].node.get_our_node_id(),
+               nodes[3].node.get_our_node_id(), nodes[4].node.get_our_node_id()],
+               &[&chan_1_2.0.contents, &chan_2_3.0.contents, &chan_3_4.0.contents],
+               &chanmon_cfgs[4].keys_manager);
+       route_params.max_total_routing_fee_msat = None;
+
+       nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
+       check_added_monitors(&nodes[0], 1);
+       pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3], &nodes[4]]], amt_msat, payment_hash, payment_secret);
+       nodes[4].node.claim_funds(payment_preimage);
+       let expected_path = &[&nodes[1], &nodes[2], &nodes[3], &nodes[4]];
+       let expected_route = &[&expected_path[..]];
+       let mut args = ClaimAlongRouteArgs::new(&nodes[0], &expected_route[..], payment_preimage)
+               .allow_1_msat_fee_overpay();
+       let expected_fee = pass_claimed_payment_along_route(args);
+       expect_payment_sent(&nodes[0], payment_preimage, Some(Some(expected_fee)), true, true);
+}
index 4d9316db79f8f9157aff3989001a116283e23d82..2e95f5c63ff7e173bb5a107f0719fb89ca3e3301 100644 (file)
@@ -3405,7 +3405,7 @@ fn do_test_reload_mon_update_completion_actions(close_during_reload: bool) {
        let mut events = nodes[1].node.get_and_clear_pending_events();
        assert_eq!(events.len(), if close_during_reload { 2 } else { 1 });
        expect_payment_forwarded(events.pop().unwrap(), &nodes[1], &nodes[0], &nodes[2], Some(1000),
-               None, close_during_reload, false);
+               None, close_during_reload, false, false);
        if close_during_reload {
                match events[0] {
                        Event::ChannelClosed { .. } => {},
index 028b100d3453c76fe9d03e4bc4b7fce7c8f0cc7c..33df5303814da439f5f83fd33a2acf53963b115f 100644 (file)
@@ -4086,7 +4086,7 @@ impl<SP: Deref> Channel<SP> where
 
                log_info!(logger, "Received channel_ready from peer for channel {}", &self.context.channel_id());
 
-               Ok(self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height(), logger))
+               Ok(self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height, logger))
        }
 
        pub fn update_add_htlc<F, FE: Deref, L: Deref>(
@@ -5475,7 +5475,7 @@ impl<SP: Deref> Channel<SP> where
 
                let shutdown_msg = self.get_outbound_shutdown();
 
-               let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height(), logger);
+               let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height, logger);
 
                if matches!(self.context.channel_state, ChannelState::AwaitingChannelReady(_)) {
                        // If we're waiting on a monitor update, we shouldn't re-send any channel_ready's.
index abfca35f9724765675206a2b903b4683aa746758..6426f0925f31f7bb1aafda8c827354fed67b1ad6 100644 (file)
@@ -58,10 +58,11 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
 use crate::ln::outbound_payment;
 use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
 use crate::ln::wire::Encode;
-use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, InvoiceBuilder};
+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::{DerivedMetadata, Offer, OfferBuilder};
+use crate::offers::offer::{Offer, OfferBuilder};
 use crate::offers::parse::Bolt12SemanticError;
 use crate::offers::refund::{Refund, RefundBuilder};
 use crate::onion_message::messenger::{Destination, MessageRouter, PendingOnionMessage, new_pending_onion_message};
@@ -77,11 +78,17 @@ use crate::util::logger::{Level, Logger, WithContext};
 use crate::util::errors::APIError;
 #[cfg(not(c_bindings))]
 use {
+       crate::offers::offer::DerivedMetadata,
        crate::routing::router::DefaultRouter,
        crate::routing::gossip::NetworkGraph,
        crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters},
        crate::sign::KeysManager,
 };
+#[cfg(c_bindings)]
+use {
+       crate::offers::offer::OfferWithDerivedMetadataBuilder,
+       crate::offers::refund::RefundMaybeWithDerivedMetadataBuilder,
+};
 
 use alloc::collections::{btree_map, BTreeMap};
 
@@ -2209,7 +2216,7 @@ macro_rules! handle_monitor_update_completion {
                let logger = WithChannelContext::from(&$self.logger, &$chan.context);
                let mut updates = $chan.monitor_updating_restored(&&logger,
                        &$self.node_signer, $self.chain_hash, &$self.default_configuration,
-                       $self.best_block.read().unwrap().height());
+                       $self.best_block.read().unwrap().height);
                let counterparty_node_id = $chan.context.get_counterparty_node_id();
                let channel_update = if updates.channel_ready.is_some() && $chan.context.is_usable() {
                        // We only send a channel_update in the case where we are just now sending a
@@ -2509,7 +2516,7 @@ where
        }
 
        fn create_and_insert_outbound_scid_alias(&self) -> u64 {
-               let height = self.best_block.read().unwrap().height();
+               let height = self.best_block.read().unwrap().height;
                let mut outbound_scid_alias = 0;
                let mut i = 0;
                loop {
@@ -2587,7 +2594,7 @@ where
                        let config = if override_config.is_some() { override_config.as_ref().unwrap() } else { &self.default_configuration };
                        match OutboundV1Channel::new(&self.fee_estimator, &self.entropy_source, &self.signer_provider, their_network_key,
                                their_features, channel_value_satoshis, push_msat, user_channel_id, config,
-                               self.best_block.read().unwrap().height(), outbound_scid_alias, temporary_channel_id)
+                               self.best_block.read().unwrap().height, outbound_scid_alias, temporary_channel_id)
                        {
                                Ok(res) => res,
                                Err(e) => {
@@ -2626,7 +2633,7 @@ where
                // the same channel.
                let mut res = Vec::with_capacity(self.short_to_chan_info.read().unwrap().len());
                {
-                       let best_block_height = self.best_block.read().unwrap().height();
+                       let best_block_height = self.best_block.read().unwrap().height;
                        let per_peer_state = self.per_peer_state.read().unwrap();
                        for (_cp_id, peer_state_mutex) in per_peer_state.iter() {
                                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
@@ -2659,7 +2666,7 @@ where
                // the same channel.
                let mut res = Vec::with_capacity(self.short_to_chan_info.read().unwrap().len());
                {
-                       let best_block_height = self.best_block.read().unwrap().height();
+                       let best_block_height = self.best_block.read().unwrap().height;
                        let per_peer_state = self.per_peer_state.read().unwrap();
                        for (_cp_id, peer_state_mutex) in per_peer_state.iter() {
                                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
@@ -2689,7 +2696,7 @@ where
 
        /// Gets the list of channels we have with a given counterparty, in random order.
        pub fn list_channels_with_counterparty(&self, counterparty_node_id: &PublicKey) -> Vec<ChannelDetails> {
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                let per_peer_state = self.per_peer_state.read().unwrap();
 
                if let Some(peer_state_mutex) = per_peer_state.get(counterparty_node_id) {
@@ -3199,7 +3206,7 @@ where
                                None
                        };
 
-                       let cur_height = self.best_block.read().unwrap().height() + 1;
+                       let cur_height = self.best_block.read().unwrap().height + 1;
 
                        if let Err((err_msg, code)) = check_incoming_htlc_cltv(
                                cur_height, outgoing_cltv_value, msg.cltv_expiry
@@ -3276,7 +3283,7 @@ where
                match decoded_hop {
                        onion_utils::Hop::Receive(next_hop_data) => {
                                // OUR PAYMENT!
-                               let current_height: u32 = self.best_block.read().unwrap().height();
+                               let current_height: u32 = self.best_block.read().unwrap().height;
                                match create_recv_pending_htlc_info(next_hop_data, shared_secret, msg.payment_hash,
                                        msg.amount_msat, msg.cltv_expiry, None, allow_underpay, msg.skimmed_fee_msat,
                                        current_height, self.default_configuration.accept_mpp_keysend)
@@ -3536,7 +3543,7 @@ where
        /// [`PeerManager::process_events`]: crate::ln::peer_handler::PeerManager::process_events
        /// [`ChannelMonitorUpdateStatus::InProgress`]: crate::chain::ChannelMonitorUpdateStatus::InProgress
        pub fn send_payment_with_route(&self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId) -> Result<(), PaymentSendFailure> {
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments
                        .send_payment_with_route(route, payment_hash, recipient_onion, payment_id,
@@ -3547,7 +3554,7 @@ where
        /// Similar to [`ChannelManager::send_payment_with_route`], but will automatically find a route based on
        /// `route_params` and retry failed payment paths based on `retry_strategy`.
        pub fn send_payment(&self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId, route_params: RouteParameters, retry_strategy: Retry) -> Result<(), RetryableSendFailure> {
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments
                        .send_payment(payment_hash, recipient_onion, payment_id, retry_strategy, route_params,
@@ -3558,7 +3565,7 @@ where
 
        #[cfg(test)]
        pub(super) fn test_send_payment_internal(&self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: Vec<[u8; 32]>) -> Result<(), PaymentSendFailure> {
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments.test_send_payment_internal(route, payment_hash, recipient_onion,
                        keysend_preimage, payment_id, recv_value_msat, onion_session_privs, &self.node_signer,
@@ -3567,7 +3574,7 @@ where
 
        #[cfg(test)]
        pub(crate) fn test_add_new_pending_payment(&self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId, route: &Route) -> Result<Vec<[u8; 32]>, PaymentSendFailure> {
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                self.pending_outbound_payments.test_add_new_pending_payment(payment_hash, recipient_onion, payment_id, route, None, &self.entropy_source, best_block_height)
        }
 
@@ -3577,7 +3584,7 @@ where
        }
 
        pub(super) fn send_payment_for_bolt12_invoice(&self, invoice: &Bolt12Invoice, payment_id: PaymentId) -> Result<(), Bolt12PaymentError> {
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments
                        .send_payment_for_bolt12_invoice(
@@ -3634,7 +3641,7 @@ where
        ///
        /// [`send_payment`]: Self::send_payment
        pub fn send_spontaneous_payment(&self, route: &Route, payment_preimage: Option<PaymentPreimage>, recipient_onion: RecipientOnionFields, payment_id: PaymentId) -> Result<PaymentHash, PaymentSendFailure> {
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments.send_spontaneous_payment_with_route(
                        route, payment_preimage, recipient_onion, payment_id, &self.entropy_source,
@@ -3649,7 +3656,7 @@ where
        ///
        /// [`PaymentParameters::for_keysend`]: crate::routing::router::PaymentParameters::for_keysend
        pub fn send_spontaneous_payment_with_retry(&self, payment_preimage: Option<PaymentPreimage>, recipient_onion: RecipientOnionFields, payment_id: PaymentId, route_params: RouteParameters, retry_strategy: Retry) -> Result<PaymentHash, RetryableSendFailure> {
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments.send_spontaneous_payment(payment_preimage, recipient_onion,
                        payment_id, retry_strategy, route_params, &self.router, self.list_usable_channels(),
@@ -3661,7 +3668,7 @@ where
        /// [`PaymentHash`] of probes based on a static secret and a random [`PaymentId`], which allows
        /// us to easily discern them from real payments.
        pub fn send_probe(&self, path: Path) -> Result<(PaymentHash, PaymentId), PaymentSendFailure> {
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments.send_probe(path, self.probing_cookie_secret,
                        &self.entropy_source, &self.node_signer, best_block_height,
@@ -3939,7 +3946,7 @@ where
                        }));
                }
                {
-                       let height = self.best_block.read().unwrap().height();
+                       let height = self.best_block.read().unwrap().height;
                        // Transactions are evaluated as final by network mempools if their locktime is strictly
                        // lower than the next block height. However, the modules constituting our Lightning
                        // node might not have perfect sync about their blockchain views. Thus, if the wallet
@@ -4363,7 +4370,7 @@ where
                                                                                                };
                                                                                                match next_hop {
                                                                                                        onion_utils::Hop::Receive(hop_data) => {
-                                                                                                               let current_height: u32 = self.best_block.read().unwrap().height();
+                                                                                                               let current_height: u32 = self.best_block.read().unwrap().height;
                                                                                                                match create_recv_pending_htlc_info(hop_data,
                                                                                                                        incoming_shared_secret, payment_hash, outgoing_amt_msat,
                                                                                                                        outgoing_cltv_value, Some(phantom_shared_secret), false, None,
@@ -4560,7 +4567,7 @@ where
                                                                                debug_assert!(!committed_to_claimable);
                                                                                let mut htlc_msat_height_data = $htlc.value.to_be_bytes().to_vec();
                                                                                htlc_msat_height_data.extend_from_slice(
-                                                                                       &self.best_block.read().unwrap().height().to_be_bytes(),
+                                                                                       &self.best_block.read().unwrap().height.to_be_bytes(),
                                                                                );
                                                                                failed_forwards.push((HTLCSource::PreviousHopData(HTLCPreviousHopData {
                                                                                                short_channel_id: $htlc.prev_hop.short_channel_id,
@@ -4698,7 +4705,7 @@ where
                                                                                                        }
                                                                                                };
                                                                                                if let Some(min_final_cltv_expiry_delta) = min_final_cltv_expiry_delta {
-                                                                                                       let expected_min_expiry_height = (self.current_best_block().height() + min_final_cltv_expiry_delta as u32) as u64;
+                                                                                                       let expected_min_expiry_height = (self.current_best_block().height + min_final_cltv_expiry_delta as u32) as u64;
                                                                                                        if (cltv_expiry as u64) < expected_min_expiry_height {
                                                                                                                log_trace!(self.logger, "Failing new HTLC with payment_hash {} as its CLTV expiry was too soon (had {}, earliest expected {})",
                                                                                                                        &payment_hash, cltv_expiry, expected_min_expiry_height);
@@ -4752,7 +4759,7 @@ where
                        }
                }
 
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                self.pending_outbound_payments.check_retry_payments(&self.router, || self.list_usable_channels(),
                        || self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, best_block_height,
                        &self.pending_events, &self.logger, |args| self.send_payment_along_path(args));
@@ -5223,7 +5230,7 @@ where
                        FailureCode::RequiredNodeFeatureMissing => HTLCFailReason::from_failure_code(failure_code.into()),
                        FailureCode::IncorrectOrUnknownPaymentDetails => {
                                let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec();
-                               htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height().to_be_bytes());
+                               htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height.to_be_bytes());
                                HTLCFailReason::reason(failure_code.into(), htlc_msat_height_data)
                        },
                        FailureCode::InvalidOnionPayload(data) => {
@@ -5554,7 +5561,7 @@ where
                if !valid_mpp {
                        for htlc in sources.drain(..) {
                                let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec();
-                               htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height().to_be_bytes());
+                               htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height.to_be_bytes());
                                let source = HTLCSource::PreviousHopData(htlc.prev_hop);
                                let reason = HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data);
                                let receiver = HTLCDestination::FailedPayment { payment_hash };
@@ -6100,7 +6107,7 @@ where
                // succeed.
                let mut channel = match peer_state.inbound_channel_request_by_id.remove(temporary_channel_id) {
                        Some(unaccepted_channel) => {
-                               let best_block_height = self.best_block.read().unwrap().height();
+                               let best_block_height = self.best_block.read().unwrap().height;
                                InboundV1Channel::new(&self.fee_estimator, &self.entropy_source, &self.signer_provider,
                                        counterparty_node_id.clone(), &self.channel_type_features(), &peer_state.latest_features,
                                        &unaccepted_channel.open_channel_msg, user_channel_id, &self.default_configuration, best_block_height,
@@ -6175,7 +6182,7 @@ where
        fn peers_without_funded_channels<Filter>(&self, maybe_count_peer: Filter) -> usize
        where Filter: Fn(&PeerState<SP>) -> bool {
                let mut peers_without_funded_channels = 0;
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                {
                        let peer_state_lock = self.per_peer_state.read().unwrap();
                        for (_, peer_mtx) in peer_state_lock.iter() {
@@ -6277,7 +6284,7 @@ where
                                msg.common_fields.temporary_channel_id.clone()));
                }
 
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                if Self::unfunded_channel_count(peer_state, best_block_height) >= MAX_UNFUNDED_CHANS_PER_PEER {
                        return Err(MsgHandleErrInternal::send_err_msg_no_close(
                                format!("Refusing more than {} unfunded channels.", MAX_UNFUNDED_CHANS_PER_PEER),
@@ -7170,7 +7177,7 @@ where
 
                                        peer_state.pending_msg_events.push(events::MessageSendEvent::BroadcastChannelAnnouncement {
                                                msg: try_chan_phase_entry!(self, chan.announcement_signatures(
-                                                       &self.node_signer, self.chain_hash, self.best_block.read().unwrap().height(),
+                                                       &self.node_signer, self.chain_hash, self.best_block.read().unwrap().height,
                                                        msg, &self.default_configuration
                                                ), chan_phase_entry),
                                                // Note that announcement_signatures fails if the channel cannot be announced,
@@ -7633,7 +7640,9 @@ where
                        self.finish_close_channel(failure);
                }
        }
+}
 
+macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
        /// Creates an [`OfferBuilder`] such that the [`Offer`] it builds is recognized by the
        /// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. The offer will
        /// not have an expiration unless otherwise set on the builder.
@@ -7662,23 +7671,25 @@ where
        /// [`Offer`]: crate::offers::offer::Offer
        /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
        pub fn create_offer_builder(
-               &self, description: String
-       ) -> Result<OfferBuilder<DerivedMetadata, secp256k1::All>, Bolt12SemanticError> {
-               let node_id = self.get_our_node_id();
-               let expanded_key = &self.inbound_payment_key;
-               let entropy = &*self.entropy_source;
-               let secp_ctx = &self.secp_ctx;
-
-               let path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
+               &$self, description: String
+       ) -> Result<$builder, Bolt12SemanticError> {
+               let node_id = $self.get_our_node_id();
+               let expanded_key = &$self.inbound_payment_key;
+               let entropy = &*$self.entropy_source;
+               let secp_ctx = &$self.secp_ctx;
+
+               let path = $self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
                let builder = OfferBuilder::deriving_signing_pubkey(
                        description, node_id, expanded_key, entropy, secp_ctx
                )
-                       .chain_hash(self.chain_hash)
+                       .chain_hash($self.chain_hash)
                        .path(path);
 
-               Ok(builder)
+               Ok(builder.into())
        }
+} }
 
+macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
        /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
        /// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund.
        ///
@@ -7728,31 +7739,55 @@ where
        /// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
        /// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments
        pub fn create_refund_builder(
-               &self, description: String, amount_msats: u64, absolute_expiry: Duration,
+               &$self, description: String, amount_msats: u64, absolute_expiry: Duration,
                payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
-       ) -> Result<RefundBuilder<secp256k1::All>, Bolt12SemanticError> {
-               let node_id = self.get_our_node_id();
-               let expanded_key = &self.inbound_payment_key;
-               let entropy = &*self.entropy_source;
-               let secp_ctx = &self.secp_ctx;
+       ) -> Result<$builder, Bolt12SemanticError> {
+               let node_id = $self.get_our_node_id();
+               let expanded_key = &$self.inbound_payment_key;
+               let entropy = &*$self.entropy_source;
+               let secp_ctx = &$self.secp_ctx;
 
-               let path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
+               let path = $self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
                let builder = RefundBuilder::deriving_payer_id(
                        description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
                )?
-                       .chain_hash(self.chain_hash)
+                       .chain_hash($self.chain_hash)
                        .absolute_expiry(absolute_expiry)
                        .path(path);
 
+               let _persistence_guard = PersistenceNotifierGuard::notify_on_drop($self);
+
                let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry);
-               self.pending_outbound_payments
+               $self.pending_outbound_payments
                        .add_new_awaiting_invoice(
                                payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
                        )
                        .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
 
-               Ok(builder)
+               Ok(builder.into())
        }
+} }
+
+impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref> ChannelManager<M, T, ES, NS, SP, F, R, L>
+where
+       M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
+       T::Target: BroadcasterInterface,
+       ES::Target: EntropySource,
+       NS::Target: NodeSigner,
+       SP::Target: SignerProvider,
+       F::Target: FeeEstimator,
+       R::Target: Router,
+       L::Target: Logger,
+{
+       #[cfg(not(c_bindings))]
+       create_offer_builder!(self, OfferBuilder<DerivedMetadata, secp256k1::All>);
+       #[cfg(not(c_bindings))]
+       create_refund_builder!(self, RefundBuilder<secp256k1::All>);
+
+       #[cfg(c_bindings)]
+       create_offer_builder!(self, OfferWithDerivedMetadataBuilder);
+       #[cfg(c_bindings)]
+       create_refund_builder!(self, RefundMaybeWithDerivedMetadataBuilder);
 
        /// Pays for an [`Offer`] using the given parameters by creating an [`InvoiceRequest`] and
        /// enqueuing it to be sent via an onion message. [`ChannelManager`] will pay the actual
@@ -7797,6 +7832,7 @@ where
        /// Errors if:
        /// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
        /// - the provided parameters are invalid for the offer,
+       /// - the offer is for an unsupported chain, or
        /// - the parameterized [`Router`] is unable to create a blinded reply path for the invoice
        ///   request.
        ///
@@ -7816,9 +7852,11 @@ where
                let entropy = &*self.entropy_source;
                let secp_ctx = &self.secp_ctx;
 
-               let builder = offer
+               let builder: InvoiceRequestBuilder<DerivedPayerId, secp256k1::All> = offer
                        .request_invoice_deriving_payer_id(expanded_key, entropy, secp_ctx, payment_id)?
-                       .chain_hash(self.chain_hash)?;
+                       .into();
+               let builder = builder.chain_hash(self.chain_hash)?;
+
                let builder = match quantity {
                        None => builder,
                        Some(quantity) => builder.quantity(quantity)?,
@@ -7834,6 +7872,8 @@ where
                let invoice_request = builder.build_and_sign()?;
                let reply_path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
 
+               let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
+
                let expiration = StaleExpiration::TimerTicks(1);
                self.pending_outbound_payments
                        .add_new_awaiting_invoice(
@@ -7883,8 +7923,10 @@ where
        ///
        /// # Errors
        ///
-       /// Errors if the parameterized [`Router`] is unable to create a blinded payment path or reply
-       /// path for the invoice.
+       /// Errors if:
+       /// - the refund is for an unsupported chain, or
+       /// - the parameterized [`Router`] is unable to create a blinded payment path or reply path for
+       ///   the invoice.
        ///
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        pub fn request_refund_payment(&self, refund: &Refund) -> Result<(), Bolt12SemanticError> {
@@ -7895,6 +7937,12 @@ where
                let amount_msats = refund.amount_msats();
                let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
 
+               if refund.chain() != self.chain_hash {
+                       return Err(Bolt12SemanticError::UnsupportedChain);
+               }
+
+               let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
+
                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)
@@ -7912,6 +7960,7 @@ where
                                let builder = refund.respond_using_derived_keys_no_std(
                                        payment_paths, payment_hash, created_at, expanded_key, entropy
                                )?;
+                               let builder: InvoiceBuilder<DerivedSigningPubkey> = builder.into();
                                let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
                                let reply_path = self.create_blinded_path()
                                        .map_err(|_| Bolt12SemanticError::MissingPaths)?;
@@ -8068,7 +8117,7 @@ where
 
                let first_hops = self.list_usable_channels();
                let payee_node_id = self.get_our_node_id();
-               let max_cltv_expiry = self.best_block.read().unwrap().height() + CLTV_FAR_FAR_AWAY
+               let max_cltv_expiry = self.best_block.read().unwrap().height + CLTV_FAR_FAR_AWAY
                        + LATENCY_GRACE_PERIOD_BLOCKS;
                let payee_tlvs = ReceiveTlvs {
                        payment_secret,
@@ -8087,7 +8136,7 @@ where
        ///
        /// [phantom node payments]: crate::sign::PhantomKeysManager
        pub fn get_phantom_scid(&self) -> u64 {
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                let short_to_chan_info = self.short_to_chan_info.read().unwrap();
                loop {
                        let scid_candidate = fake_scid::Namespace::Phantom.get_fake_scid(best_block_height, &self.chain_hash, &self.fake_scid_rand_bytes, &self.entropy_source);
@@ -8117,7 +8166,7 @@ where
        /// Note that this method is not guaranteed to return unique values, you may need to call it a few
        /// times to get a unique scid.
        pub fn get_intercept_scid(&self) -> u64 {
-               let best_block_height = self.best_block.read().unwrap().height();
+               let best_block_height = self.best_block.read().unwrap().height;
                let short_to_chan_info = self.short_to_chan_info.read().unwrap();
                loop {
                        let scid_candidate = fake_scid::Namespace::Intercept.get_fake_scid(best_block_height, &self.chain_hash, &self.fake_scid_rand_bytes, &self.entropy_source);
@@ -8365,9 +8414,9 @@ where
        fn filtered_block_connected(&self, header: &Header, txdata: &TransactionData, height: u32) {
                {
                        let best_block = self.best_block.read().unwrap();
-                       assert_eq!(best_block.block_hash(), header.prev_blockhash,
+                       assert_eq!(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!(best_block.height(), height - 1,
+                       assert_eq!(best_block.height, height - 1,
                                "Blocks must be connected in chain-order - the connected block height must be one greater than the previous height");
                }
 
@@ -8382,9 +8431,9 @@ where
                let new_height = height - 1;
                {
                        let mut best_block = self.best_block.write().unwrap();
-                       assert_eq!(best_block.block_hash(), header.block_hash(),
+                       assert_eq!(best_block.block_hash, header.block_hash(),
                                "Blocks must be disconnected in chain-order - the disconnected header must be the last connected header");
-                       assert_eq!(best_block.height(), height,
+                       assert_eq!(best_block.height, height,
                                "Blocks must be disconnected in chain-order - the disconnected block must have the correct height");
                        *best_block = BestBlock::new(header.prev_blockhash, new_height)
                }
@@ -8418,7 +8467,7 @@ where
                self.do_chain_event(Some(height), |channel| channel.transactions_confirmed(&block_hash, height, txdata, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context))
                        .map(|(a, b)| (a, Vec::new(), b)));
 
-               let last_best_block_height = self.best_block.read().unwrap().height();
+               let last_best_block_height = self.best_block.read().unwrap().height;
                if height < last_best_block_height {
                        let timestamp = self.highest_seen_timestamp.load(Ordering::Acquire);
                        self.do_chain_event(Some(last_best_block_height), |channel| channel.best_block_updated(last_best_block_height, timestamp as u32, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context)));
@@ -9118,7 +9167,7 @@ where
                                                let mut peer_state = e.get().lock().unwrap();
                                                peer_state.latest_features = init_msg.features.clone();
 
-                                               let best_block_height = self.best_block.read().unwrap().height();
+                                               let best_block_height = self.best_block.read().unwrap().height;
                                                if inbound_peer_limited &&
                                                        Self::unfunded_channel_count(&*peer_state, best_block_height) ==
                                                        peer_state.channel_by_id.len()
@@ -9193,8 +9242,6 @@ where
        }
 
        fn handle_error(&self, counterparty_node_id: &PublicKey, msg: &msgs::ErrorMessage) {
-               let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
                match &msg.data as &str {
                        "cannot co-op close channel w/ active htlcs"|
                        "link failed to shutdown" =>
@@ -9207,34 +9254,45 @@ where
                                // We're not going to bother handling this in a sensible way, instead simply
                                // repeating the Shutdown message on repeat until morale improves.
                                if !msg.channel_id.is_zero() {
-                                       let per_peer_state = self.per_peer_state.read().unwrap();
-                                       let peer_state_mutex_opt = per_peer_state.get(counterparty_node_id);
-                                       if peer_state_mutex_opt.is_none() { return; }
-                                       let mut peer_state = peer_state_mutex_opt.unwrap().lock().unwrap();
-                                       if let Some(ChannelPhase::Funded(chan)) = peer_state.channel_by_id.get(&msg.channel_id) {
-                                               if let Some(msg) = chan.get_outbound_shutdown() {
-                                                       peer_state.pending_msg_events.push(events::MessageSendEvent::SendShutdown {
-                                                               node_id: *counterparty_node_id,
-                                                               msg,
-                                                       });
-                                               }
-                                               peer_state.pending_msg_events.push(events::MessageSendEvent::HandleError {
-                                                       node_id: *counterparty_node_id,
-                                                       action: msgs::ErrorAction::SendWarningMessage {
-                                                               msg: msgs::WarningMessage {
-                                                                       channel_id: msg.channel_id,
-                                                                       data: "You appear to be exhibiting LND bug 6039, we'll keep sending you shutdown messages until you handle them correctly".to_owned()
-                                                               },
-                                                               log_level: Level::Trace,
+                                       PersistenceNotifierGuard::optionally_notify(
+                                               self,
+                                               || -> NotifyOption {
+                                                       let per_peer_state = self.per_peer_state.read().unwrap();
+                                                       let peer_state_mutex_opt = per_peer_state.get(counterparty_node_id);
+                                                       if peer_state_mutex_opt.is_none() { return NotifyOption::SkipPersistNoEvents; }
+                                                       let mut peer_state = peer_state_mutex_opt.unwrap().lock().unwrap();
+                                                       if let Some(ChannelPhase::Funded(chan)) = peer_state.channel_by_id.get(&msg.channel_id) {
+                                                               if let Some(msg) = chan.get_outbound_shutdown() {
+                                                                       peer_state.pending_msg_events.push(events::MessageSendEvent::SendShutdown {
+                                                                               node_id: *counterparty_node_id,
+                                                                               msg,
+                                                                       });
+                                                               }
+                                                               peer_state.pending_msg_events.push(events::MessageSendEvent::HandleError {
+                                                                       node_id: *counterparty_node_id,
+                                                                       action: msgs::ErrorAction::SendWarningMessage {
+                                                                               msg: msgs::WarningMessage {
+                                                                                       channel_id: msg.channel_id,
+                                                                                       data: "You appear to be exhibiting LND bug 6039, we'll keep sending you shutdown messages until you handle them correctly".to_owned()
+                                                                               },
+                                                                               log_level: Level::Trace,
+                                                                       }
+                                                               });
+                                                               // This can happen in a fairly tight loop, so we absolutely cannot trigger
+                                                               // a `ChannelManager` write here.
+                                                               return NotifyOption::SkipPersistHandleEvents;
                                                        }
-                                               });
-                                       }
+                                                       NotifyOption::SkipPersistNoEvents
+                                               }
+                                       );
                                }
                                return;
                        }
                        _ => {}
                }
 
+               let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
+
                if msg.channel_id.is_zero() {
                        let channel_ids: Vec<ChannelId> = {
                                let per_peer_state = self.per_peer_state.read().unwrap();
@@ -9424,6 +9482,8 @@ 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())),
@@ -9435,18 +9495,25 @@ 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()))
-                                               .and_then(|invoice|
-                                                       match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) {
+                                               .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(
+                                                               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),
@@ -10060,8 +10127,8 @@ where
                self.chain_hash.write(writer)?;
                {
                        let best_block = self.best_block.read().unwrap();
-                       best_block.height().write(writer)?;
-                       best_block.block_hash().write(writer)?;
+                       best_block.height.write(writer)?;
+                       best_block.block_hash.write(writer)?;
                }
 
                let mut serializable_peer_count: u64 = 0;
@@ -12343,7 +12410,7 @@ mod tests {
                };
                // Check that if the amount we received + the penultimate hop extra fee is less than the sender
                // intended amount, we fail the payment.
-               let current_height: u32 = node[0].node.best_block.read().unwrap().height();
+               let current_height: u32 = node[0].node.best_block.read().unwrap().height;
                if let Err(crate::ln::channelmanager::InboundHTLCErr { err_code, .. }) =
                        create_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
                                sender_intended_amt_msat - extra_fee_msat - 1, 42, None, true, Some(extra_fee_msat),
@@ -12363,7 +12430,7 @@ mod tests {
                        }),
                        custom_tlvs: Vec::new(),
                };
-               let current_height: u32 = node[0].node.best_block.read().unwrap().height();
+               let current_height: u32 = node[0].node.best_block.read().unwrap().height;
                assert!(create_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
                        sender_intended_amt_msat - extra_fee_msat, 42, None, true, Some(extra_fee_msat),
                        current_height, node[0].node.default_configuration.accept_mpp_keysend).is_ok());
@@ -12376,7 +12443,7 @@ mod tests {
                let node_chanmgr = create_node_chanmgrs(1, &node_cfg, &[None]);
                let node = create_network(1, &node_cfg, &node_chanmgr);
 
-               let current_height: u32 = node[0].node.best_block.read().unwrap().height();
+               let current_height: u32 = node[0].node.best_block.read().unwrap().height;
                let result = create_recv_pending_htlc_info(msgs::InboundOnionPayload::Receive {
                        sender_intended_htlc_amt_msat: 100,
                        cltv_expiry_height: 22,
@@ -12802,7 +12869,7 @@ pub mod bench {
 
                assert_eq!(&tx_broadcaster.txn_broadcasted.lock().unwrap()[..], &[tx.clone()]);
 
-               let block = create_dummy_block(BestBlock::from_network(network).block_hash(), 42, vec![tx]);
+               let block = create_dummy_block(BestBlock::from_network(network).block_hash, 42, vec![tx]);
                Listen::block_connected(&node_a, &block, 1);
                Listen::block_connected(&node_b, &block, 1);
 
index adf2768b4152700ed82172e9a44ab75c6c3475a2..cb310ec95398a38309840a3177966102da3eeebb 100644 (file)
@@ -2223,17 +2223,26 @@ macro_rules! expect_payment_path_successful {
        }
 }
 
+/// Returns the total fee earned by this HTLC forward, in msat.
 pub fn expect_payment_forwarded<CM: AChannelManager, H: NodeHolder<CM=CM>>(
        event: Event, node: &H, prev_node: &H, next_node: &H, expected_fee: Option<u64>,
        expected_extra_fees_msat: Option<u64>, upstream_force_closed: bool,
-       downstream_force_closed: bool
-) {
+       downstream_force_closed: bool, allow_1_msat_fee_overpay: bool,
+) -> Option<u64> {
        match event {
                Event::PaymentForwarded {
                        total_fee_earned_msat, prev_channel_id, claim_from_onchain_tx, next_channel_id,
                        outbound_amount_forwarded_msat: _, skimmed_fee_msat
                } => {
-                       assert_eq!(total_fee_earned_msat, expected_fee);
+                       if allow_1_msat_fee_overpay {
+                               // Aggregating fees for blinded paths may result in a rounding error, causing slight
+                               // overpayment in fees.
+                               let actual_fee = total_fee_earned_msat.unwrap();
+                               let expected_fee = expected_fee.unwrap();
+                               assert!(actual_fee == expected_fee || actual_fee == expected_fee + 1);
+                       } else {
+                               assert_eq!(total_fee_earned_msat, expected_fee);
+                       }
 
                        // Check that the (knowingly) withheld amount is always less or equal to the expected
                        // overpaid amount.
@@ -2248,6 +2257,7 @@ pub fn expect_payment_forwarded<CM: AChannelManager, H: NodeHolder<CM=CM>>(
                                assert!(node.node().list_channels().iter().any(|x| x.counterparty.node_id == next_node.node().get_our_node_id() && x.channel_id == next_channel_id.unwrap()));
                        }
                        assert_eq!(claim_from_onchain_tx, downstream_force_closed);
+                       total_fee_earned_msat
                },
                _ => panic!("Unexpected event"),
        }
@@ -2260,7 +2270,7 @@ macro_rules! expect_payment_forwarded {
                assert_eq!(events.len(), 1);
                $crate::ln::functional_test_utils::expect_payment_forwarded(
                        events.pop().unwrap(), &$node, &$prev_node, &$next_node, $expected_fee, None,
-                       $upstream_force_closed, $downstream_force_closed
+                       $upstream_force_closed, $downstream_force_closed, false
                );
        }
 }
@@ -2472,7 +2482,60 @@ fn fail_payment_along_path<'a, 'b, 'c>(expected_path: &[&Node<'a, 'b, 'c>]) {
        }
 }
 
-pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>, ev: MessageSendEvent, payment_claimable_expected: bool, clear_recipient_events: bool, expected_preimage: Option<PaymentPreimage>, is_probe: bool) -> Option<Event> {
+pub struct PassAlongPathArgs<'a, 'b, 'c, 'd> {
+       pub origin_node: &'a Node<'b, 'c, 'd>,
+       pub expected_path: &'a [&'a Node<'b, 'c, 'd>],
+       pub recv_value: u64,
+       pub payment_hash: PaymentHash,
+       pub payment_secret: Option<PaymentSecret>,
+       pub event: MessageSendEvent,
+       pub payment_claimable_expected: bool,
+       pub clear_recipient_events: bool,
+       pub expected_preimage: Option<PaymentPreimage>,
+       pub is_probe: bool,
+}
+
+impl<'a, 'b, 'c, 'd> PassAlongPathArgs<'a, 'b, 'c, 'd> {
+       pub fn new(
+               origin_node: &'a Node<'b, 'c, 'd>, expected_path: &'a [&'a Node<'b, 'c, 'd>], recv_value: u64,
+               payment_hash: PaymentHash, event: MessageSendEvent,
+       ) -> Self {
+               Self {
+                       origin_node, expected_path, recv_value, payment_hash, payment_secret: None, event,
+                       payment_claimable_expected: true, clear_recipient_events: true, expected_preimage: None,
+                       is_probe: false,
+               }
+       }
+       pub fn without_clearing_recipient_events(mut self) -> Self {
+               self.clear_recipient_events = false;
+               self
+       }
+       pub fn is_probe(mut self) -> Self {
+               self.payment_claimable_expected = false;
+               self.is_probe = true;
+               self
+       }
+       pub fn without_claimable_event(mut self) -> Self {
+               self.payment_claimable_expected = false;
+               self
+       }
+       pub fn with_payment_secret(mut self, payment_secret: PaymentSecret) -> Self {
+               self.payment_secret = Some(payment_secret);
+               self
+       }
+       pub fn with_payment_preimage(mut self, payment_preimage: PaymentPreimage) -> Self {
+               self.expected_preimage = Some(payment_preimage);
+               self
+       }
+}
+
+pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option<Event> {
+       let PassAlongPathArgs {
+               origin_node, expected_path, recv_value, payment_hash: our_payment_hash,
+               payment_secret: our_payment_secret, event: ev, payment_claimable_expected,
+               clear_recipient_events, expected_preimage, is_probe
+       } = args;
+
        let mut payment_event = SendEvent::from_event(ev);
        let mut prev_node = origin_node;
        let mut event = None;
@@ -2539,7 +2602,17 @@ pub fn do_pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_p
 }
 
 pub fn pass_along_path<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: Option<PaymentSecret>, ev: MessageSendEvent, payment_claimable_expected: bool, expected_preimage: Option<PaymentPreimage>) -> Option<Event> {
-       do_pass_along_path(origin_node, expected_path, recv_value, our_payment_hash, our_payment_secret, ev, payment_claimable_expected, true, expected_preimage, false)
+       let mut args = PassAlongPathArgs::new(origin_node, expected_path, recv_value, our_payment_hash, ev);
+       if !payment_claimable_expected {
+               args = args.without_claimable_event();
+       }
+       if let Some(payment_secret) = our_payment_secret {
+               args = args.with_payment_secret(payment_secret);
+       }
+       if let Some(payment_preimage) = expected_preimage {
+               args = args.with_payment_preimage(payment_preimage);
+       }
+       do_pass_along_path(args)
 }
 
 pub fn send_probe_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&[&Node<'a, 'b, 'c>]]) {
@@ -2551,7 +2624,10 @@ pub fn send_probe_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expect
        for path in expected_route.iter() {
                let ev = remove_first_msg_event_to_node(&path[0].node.get_our_node_id(), &mut events);
 
-               do_pass_along_path(origin_node, path, 0, PaymentHash([0_u8; 32]), None, ev, false, false, None, true);
+               do_pass_along_path(PassAlongPathArgs::new(origin_node, path, 0, PaymentHash([0_u8; 32]), ev)
+                       .is_probe()
+                       .without_clearing_recipient_events());
+
                let nodes_to_fail_payment: Vec<_> = vec![origin_node].into_iter().chain(path.iter().cloned()).collect();
 
                fail_payment_along_path(nodes_to_fail_payment.as_slice());
@@ -2598,6 +2674,14 @@ pub struct ClaimAlongRouteArgs<'a, 'b, 'c, 'd> {
        pub expected_min_htlc_overpay: Vec<u32>,
        pub skip_last: bool,
        pub payment_preimage: PaymentPreimage,
+       // Allow forwarding nodes to have taken 1 msat more fee than expected based on the downstream
+       // fulfill amount.
+       //
+       // Necessary because our test utils calculate the expected fee for an intermediate node based on
+       // the amount was claimed in their downstream peer's fulfill, but blinded intermediate nodes
+       // calculate their fee based on the inbound amount from their upstream peer, causing a difference
+       // in rounding.
+       pub allow_1_msat_fee_overpay: bool,
 }
 
 impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> {
@@ -2608,6 +2692,7 @@ impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> {
                Self {
                        origin_node, expected_paths, expected_extra_fees: vec![0; expected_paths.len()],
                        expected_min_htlc_overpay: vec![0; expected_paths.len()], skip_last: false, payment_preimage,
+                       allow_1_msat_fee_overpay: false,
                }
        }
        pub fn skip_last(mut self, skip_last: bool) -> Self {
@@ -2622,15 +2707,21 @@ impl<'a, 'b, 'c, 'd> ClaimAlongRouteArgs<'a, 'b, 'c, 'd> {
                self.expected_min_htlc_overpay = extra_fees;
                self
        }
+       pub fn allow_1_msat_fee_overpay(mut self) -> Self {
+               self.allow_1_msat_fee_overpay = true;
+               self
+       }
 }
 
 pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArgs) -> u64 {
        let ClaimAlongRouteArgs {
                origin_node, expected_paths, expected_extra_fees, expected_min_htlc_overpay, skip_last,
-               payment_preimage: our_payment_preimage
+               payment_preimage: our_payment_preimage, allow_1_msat_fee_overpay,
        } = args;
        let claim_event = expected_paths[0].last().unwrap().node.get_and_clear_pending_events();
        assert_eq!(claim_event.len(), 1);
+       #[allow(unused)]
+       let mut fwd_amt_msat = 0;
        match claim_event[0] {
                Event::PaymentClaimed {
                        purpose: PaymentPurpose::SpontaneousPayment(preimage),
@@ -2647,6 +2738,7 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg
                        assert_eq!(htlcs.len(), expected_paths.len());  // One per path.
                        assert_eq!(htlcs.iter().map(|h| h.value_msat).sum::<u64>(), amount_msat);
                        expected_paths.iter().zip(htlcs).for_each(|(path, htlc)| check_claimed_htlc_channel(origin_node, path, htlc));
+                       fwd_amt_msat = amount_msat;
                },
                Event::PaymentClaimed {
                        purpose: PaymentPurpose::InvoicePayment { .. },
@@ -2659,6 +2751,7 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg
                        assert_eq!(htlcs.len(), expected_paths.len());  // One per path.
                        assert_eq!(htlcs.iter().map(|h| h.value_msat).sum::<u64>(), amount_msat);
                        expected_paths.iter().zip(htlcs).for_each(|(path, htlc)| check_claimed_htlc_channel(origin_node, path, htlc));
+                       fwd_amt_msat = amount_msat;
                }
                _ => panic!(),
        }
@@ -2690,8 +2783,12 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg
                per_path_msgs.push(msgs_from_ev!(&events[0]));
        } else {
                for expected_path in expected_paths.iter() {
-                       // For MPP payments, we always want the message to the first node in the path.
-                       let ev = remove_first_msg_event_to_node(&expected_path[0].node.get_our_node_id(), &mut events);
+                       // For MPP payments, we want the fulfill message from the payee to the penultimate hop in the
+                       // path.
+                       let penultimate_hop_node_id = expected_path.iter().rev().skip(1).next()
+                               .map(|n| n.node.get_our_node_id())
+                               .unwrap_or(origin_node.node.get_our_node_id());
+                       let ev = remove_first_msg_event_to_node(&penultimate_hop_node_id, &mut events);
                        per_path_msgs.push(msgs_from_ev!(&ev));
                }
        }
@@ -2715,15 +2812,20 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg
                                {
                                        $node.node.handle_update_fulfill_htlc(&$prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0);
                                        let mut fee = {
-                                               let per_peer_state = $node.node.per_peer_state.read().unwrap();
-                                               let peer_state = per_peer_state.get(&$prev_node.node.get_our_node_id())
-                                                       .unwrap().lock().unwrap();
-                                               let channel = peer_state.channel_by_id.get(&next_msgs.as_ref().unwrap().0.channel_id).unwrap();
-                                               if let Some(prev_config) = channel.context().prev_config() {
-                                                       prev_config.forwarding_fee_base_msat
-                                               } else {
-                                                       channel.context().config().forwarding_fee_base_msat
-                                               }
+                                               let (base_fee, prop_fee) = {
+                                                       let per_peer_state = $node.node.per_peer_state.read().unwrap();
+                                                       let peer_state = per_peer_state.get(&$prev_node.node.get_our_node_id())
+                                                               .unwrap().lock().unwrap();
+                                                       let channel = peer_state.channel_by_id.get(&next_msgs.as_ref().unwrap().0.channel_id).unwrap();
+                                                       if let Some(prev_config) = channel.context().prev_config() {
+                                                               (prev_config.forwarding_fee_base_msat as u64,
+                                                                prev_config.forwarding_fee_proportional_millionths as u64)
+                                                       } else {
+                                                               (channel.context().config().forwarding_fee_base_msat as u64,
+                                                                channel.context().config().forwarding_fee_proportional_millionths as u64)
+                                                       }
+                                               };
+                                               ((fwd_amt_msat * prop_fee / 1_000_000) + base_fee) as u32
                                        };
 
                                        let mut expected_extra_fee = None;
@@ -2734,9 +2836,10 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg
                                        }
                                        let mut events = $node.node.get_and_clear_pending_events();
                                        assert_eq!(events.len(), 1);
-                                       expect_payment_forwarded(events.pop().unwrap(), *$node, $next_node, $prev_node,
-                                               Some(fee as u64), expected_extra_fee, false, false);
-                                       expected_total_fee_msat += fee as u64;
+                                       let actual_fee = expect_payment_forwarded(events.pop().unwrap(), *$node, $next_node, $prev_node,
+                                               Some(fee as u64), expected_extra_fee, false, false, allow_1_msat_fee_overpay);
+                                       expected_total_fee_msat += actual_fee.unwrap();
+                                       fwd_amt_msat += actual_fee.unwrap();
                                        check_added_monitors!($node, 1);
                                        let new_next_msgs = if $new_msgs {
                                                let events = $node.node.get_and_clear_pending_msg_events();
index 2f71ca66a0c3e26856b844c31155fd5abd0a5284..c5ea582b27f81a95abd50bc760e26b8f30e363e5 100644 (file)
@@ -1401,7 +1401,7 @@ fn test_fee_spike_violation_fails_htlc() {
        let secp_ctx = Secp256k1::new();
        let session_priv = SecretKey::from_slice(&[42; 32]).expect("RNG is bad!");
 
-       let cur_height = nodes[1].node.best_block.read().unwrap().height() + 1;
+       let cur_height = nodes[1].node.best_block.read().unwrap().height + 1;
 
        let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
        let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0],
@@ -1599,7 +1599,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
        // Need to manually create the update_add_htlc message to go around the channel reserve check in send_htlc()
        let secp_ctx = Secp256k1::new();
        let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
-       let cur_height = nodes[1].node.best_block.read().unwrap().height() + 1;
+       let cur_height = nodes[1].node.best_block.read().unwrap().height + 1;
        let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
        let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0],
                700_000, RecipientOnionFields::secret_only(payment_secret), cur_height, &None).unwrap();
@@ -1778,7 +1778,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
        // Need to manually create the update_add_htlc message to go around the channel reserve check in send_htlc()
        let secp_ctx = Secp256k1::new();
        let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
-       let cur_height = nodes[0].node.best_block.read().unwrap().height() + 1;
+       let cur_height = nodes[0].node.best_block.read().unwrap().height + 1;
        let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route_2.paths[0], &session_priv).unwrap();
        let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(
                &route_2.paths[0], recv_value_2, RecipientOnionFields::spontaneous_empty(), cur_height, &None).unwrap();
@@ -3503,7 +3503,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
 
                let secp_ctx = Secp256k1::new();
                let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
-               let current_height = nodes[1].node.best_block.read().unwrap().height() + 1;
+               let current_height = nodes[1].node.best_block.read().unwrap().height + 1;
                let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::build_onion_payloads(
                        &route.paths[0], 50_000, RecipientOnionFields::secret_only(payment_secret), current_height, &None).unwrap();
                let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
@@ -6488,7 +6488,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
                get_route_and_payment_hash!(nodes[0], nodes[1], 1000);
        route.paths[0].hops[0].fee_msat = send_amt;
        let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
-       let cur_height = nodes[0].node.best_block.read().unwrap().height() + 1;
+       let cur_height = nodes[0].node.best_block.read().unwrap().height + 1;
        let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::signing_only(), &route.paths[0], &session_priv).unwrap();
        let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(
                &route.paths[0], send_amt, RecipientOnionFields::secret_only(our_payment_secret), cur_height, &None).unwrap();
@@ -10085,7 +10085,7 @@ fn test_non_final_funding_tx() {
        let accept_channel_message = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
        nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &accept_channel_message);
 
-       let best_height = nodes[0].node.best_block.read().unwrap().height();
+       let best_height = nodes[0].node.best_block.read().unwrap().height;
 
        let chan_id = *nodes[0].network_chan_count.borrow();
        let events = nodes[0].node.get_and_clear_pending_events();
@@ -10130,7 +10130,7 @@ fn test_non_final_funding_tx_within_headroom() {
        let accept_channel_message = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
        nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &accept_channel_message);
 
-       let best_height = nodes[0].node.best_block.read().unwrap().height();
+       let best_height = nodes[0].node.best_block.read().unwrap().height;
 
        let chan_id = *nodes[0].network_chan_count.borrow();
        let events = nodes[0].node.get_and_clear_pending_events();
index e79f4bbd24d90d481735a7c602f64442a128d296..71b73390d1c80a9590afedecfbee6f542822a076 100644 (file)
@@ -85,6 +85,8 @@ mod offers_tests;
 
 pub use self::peer_channel_encryptor::LN_MAX_MSG_LEN;
 
+use bitcoin::hashes::{sha256::Hash as Sha256, Hash};
+
 /// payment_hash type, use to cross-lock hop
 ///
 /// This is not exported to bindings users as we just use [u8; 32] directly
@@ -109,6 +111,13 @@ impl core::fmt::Display for PaymentPreimage {
        }
 }
 
+/// Converts a `PaymentPreimage` into a `PaymentHash` by hashing the preimage with SHA256.
+impl From<PaymentPreimage> for PaymentHash {
+       fn from(value: PaymentPreimage) -> Self {
+               PaymentHash(Sha256::hash(&value.0).to_byte_array())
+       }
+}
+
 /// payment_secret type, use to authenticate sender to the receiver and tie MPP HTLCs together
 ///
 /// This is not exported to bindings users as we just use [u8; 32] directly
index f0db28949ed7e4aeaf422c4b78de828c04da317c..d291ac8664aceec5fdaff3b0d2ce20077e727028 100644 (file)
@@ -843,6 +843,16 @@ impl SocketAddress {
        /// This maximum length is reached by a hostname address descriptor:
        /// a hostname with a maximum length of 255, its 1-byte length and a 2-byte port.
        pub(crate) const MAX_LEN: u16 = 258;
+
+       pub(crate) fn is_tor(&self) -> bool {
+               match self {
+                       &SocketAddress::TcpIpV4 {..} => false,
+                       &SocketAddress::TcpIpV6 {..} => false,
+                       &SocketAddress::OnionV2(_) => true,
+                       &SocketAddress::OnionV3 {..} => true,
+                       &SocketAddress::Hostname {..} => false,
+               }
+       }
 }
 
 impl Writeable for SocketAddress {
index 0038bdd79a218661161b3053f9dc22481fcb363d..e75bd2c70e1e9c3d6b68945fa0ea424a694d4f7d 100644 (file)
 //! Nodes without channels are disconnected and connected as needed to ensure that deterministic
 //! blinded paths are used.
 
+use bitcoin::network::constants::Network;
 use core::time::Duration;
 use crate::blinded_path::BlindedPath;
 use crate::events::{Event, MessageSendEventsProvider, PaymentPurpose};
 use crate::ln::channelmanager::{PaymentId, RecentPaymentDetails, Retry, self};
 use crate::ln::functional_test_utils::*;
-use crate::ln::msgs::{ChannelMessageHandler, Init, OnionMessage, OnionMessageHandler};
+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;
@@ -53,6 +54,8 @@ use crate::offers::parse::Bolt12SemanticError;
 use crate::onion_message::messenger::PeeledOnion;
 use crate::onion_message::offers::OffersMessage;
 use crate::onion_message::packet::ParsedOnionMessageContents;
+use crate::routing::gossip::{NodeAlias, NodeId};
+use crate::sign::{NodeSigner, Recipient};
 
 use crate::prelude::*;
 
@@ -98,6 +101,37 @@ fn disconnect_peers<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, peers: &[&Node<'a, 'b
        }
 }
 
+fn announce_node_address<'a, 'b, 'c>(
+       node: &Node<'a, 'b, 'c>, peers: &[&Node<'a, 'b, 'c>], address: SocketAddress,
+) {
+       let features = node.onion_messenger.provided_node_features()
+               | node.gossip_sync.provided_node_features();
+       let rgb = [0u8; 3];
+       let announcement = UnsignedNodeAnnouncement {
+               features,
+               timestamp: 1000,
+               node_id: NodeId::from_pubkey(&node.keys_manager.get_node_id(Recipient::Node).unwrap()),
+               rgb,
+               alias: NodeAlias([0u8; 32]),
+               addresses: vec![address],
+               excess_address_data: Vec::new(),
+               excess_data: Vec::new(),
+       };
+       let signature = node.keys_manager.sign_gossip_message(
+               UnsignedGossipMessage::NodeAnnouncement(&announcement)
+       ).unwrap();
+
+       let msg = NodeAnnouncement {
+               signature,
+               contents: announcement
+       };
+
+       node.gossip_sync.handle_node_announcement(&msg).unwrap();
+       for peer in peers {
+               peer.gossip_sync.handle_node_announcement(&msg).unwrap();
+       }
+}
+
 fn route_bolt12_payment<'a, 'b, 'c>(
        node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>], invoice: &Bolt12Invoice
 ) {
@@ -112,9 +146,9 @@ fn route_bolt12_payment<'a, 'b, 'c>(
        // invoice contains the payment_hash but it was encrypted inside an onion message.
        let amount_msats = invoice.amount_msats();
        let payment_hash = invoice.payment_hash();
-       do_pass_along_path(
-               node, path, amount_msats, payment_hash, None, ev, false, false, None, false
-       );
+       let args = PassAlongPathArgs::new(node, path, amount_msats, payment_hash, ev)
+               .without_clearing_recipient_events();
+       do_pass_along_path(args);
 }
 
 fn claim_bolt12_payment<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>]) {
@@ -178,6 +212,123 @@ fn extract_invoice_error<'a, 'b, 'c>(
        }
 }
 
+/// Checks that blinded paths without Tor-only nodes are preferred when constructing an offer.
+#[test]
+fn prefers_non_tor_nodes_in_blinded_paths() {
+       let mut accept_forward_cfg = test_default_channel_config();
+       accept_forward_cfg.accept_forwards_to_priv_channels = true;
+
+       let mut features = channelmanager::provided_init_features(&accept_forward_cfg);
+       features.set_onion_messages_optional();
+       features.set_route_blinding_optional();
+
+       let chanmon_cfgs = create_chanmon_cfgs(6);
+       let node_cfgs = create_node_cfgs(6, &chanmon_cfgs);
+
+       *node_cfgs[1].override_init_features.borrow_mut() = Some(features);
+
+       let node_chanmgrs = create_node_chanmgrs(
+               6, &node_cfgs, &[None, Some(accept_forward_cfg), None, None, None, None]
+       );
+       let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
+
+       create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
+       create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 10_000_000, 1_000_000_000);
+       create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 10_000_000, 1_000_000_000);
+       create_announced_chan_between_nodes_with_value(&nodes, 1, 4, 10_000_000, 1_000_000_000);
+       create_announced_chan_between_nodes_with_value(&nodes, 1, 5, 10_000_000, 1_000_000_000);
+       create_announced_chan_between_nodes_with_value(&nodes, 2, 4, 10_000_000, 1_000_000_000);
+       create_announced_chan_between_nodes_with_value(&nodes, 2, 5, 10_000_000, 1_000_000_000);
+
+       // Add an extra channel so that more than one of Bob's peers have MIN_PEER_CHANNELS.
+       create_announced_chan_between_nodes_with_value(&nodes, 4, 5, 10_000_000, 1_000_000_000);
+
+       let (alice, bob, charlie, david) = (&nodes[0], &nodes[1], &nodes[2], &nodes[3]);
+       let bob_id = bob.node.get_our_node_id();
+       let charlie_id = charlie.node.get_our_node_id();
+
+       disconnect_peers(alice, &[charlie, david, &nodes[4], &nodes[5]]);
+       disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
+
+       let tor = SocketAddress::OnionV2([255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 38, 7]);
+       announce_node_address(charlie, &[alice, bob, david, &nodes[4], &nodes[5]], tor.clone());
+
+       let offer = bob.node
+               .create_offer_builder("coffee".to_string()).unwrap()
+               .amount_msats(10_000_000)
+               .build().unwrap();
+       assert_ne!(offer.signing_pubkey(), bob_id);
+       assert!(!offer.paths().is_empty());
+       for path in offer.paths() {
+               assert_ne!(path.introduction_node_id, bob_id);
+               assert_ne!(path.introduction_node_id, charlie_id);
+       }
+
+       // Use a one-hop blinded path when Bob is announced and all his peers are Tor-only.
+       announce_node_address(&nodes[4], &[alice, bob, charlie, david, &nodes[5]], tor.clone());
+       announce_node_address(&nodes[5], &[alice, bob, charlie, david, &nodes[4]], tor.clone());
+
+       let offer = bob.node
+               .create_offer_builder("coffee".to_string()).unwrap()
+               .amount_msats(10_000_000)
+               .build().unwrap();
+       assert_ne!(offer.signing_pubkey(), bob_id);
+       assert!(!offer.paths().is_empty());
+       for path in offer.paths() {
+               assert_eq!(path.introduction_node_id, bob_id);
+       }
+}
+
+/// Checks that blinded paths prefer an introduction node that is the most connected.
+#[test]
+fn prefers_more_connected_nodes_in_blinded_paths() {
+       let mut accept_forward_cfg = test_default_channel_config();
+       accept_forward_cfg.accept_forwards_to_priv_channels = true;
+
+       let mut features = channelmanager::provided_init_features(&accept_forward_cfg);
+       features.set_onion_messages_optional();
+       features.set_route_blinding_optional();
+
+       let chanmon_cfgs = create_chanmon_cfgs(6);
+       let node_cfgs = create_node_cfgs(6, &chanmon_cfgs);
+
+       *node_cfgs[1].override_init_features.borrow_mut() = Some(features);
+
+       let node_chanmgrs = create_node_chanmgrs(
+               6, &node_cfgs, &[None, Some(accept_forward_cfg), None, None, None, None]
+       );
+       let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
+
+       create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
+       create_unannounced_chan_between_nodes_with_value(&nodes, 2, 3, 10_000_000, 1_000_000_000);
+       create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 10_000_000, 1_000_000_000);
+       create_announced_chan_between_nodes_with_value(&nodes, 1, 4, 10_000_000, 1_000_000_000);
+       create_announced_chan_between_nodes_with_value(&nodes, 1, 5, 10_000_000, 1_000_000_000);
+       create_announced_chan_between_nodes_with_value(&nodes, 2, 4, 10_000_000, 1_000_000_000);
+       create_announced_chan_between_nodes_with_value(&nodes, 2, 5, 10_000_000, 1_000_000_000);
+
+       // Add extra channels so that more than one of Bob's peers have MIN_PEER_CHANNELS and one has
+       // more than the others.
+       create_announced_chan_between_nodes_with_value(&nodes, 0, 4, 10_000_000, 1_000_000_000);
+       create_announced_chan_between_nodes_with_value(&nodes, 3, 4, 10_000_000, 1_000_000_000);
+
+       let (alice, bob, charlie, david) = (&nodes[0], &nodes[1], &nodes[2], &nodes[3]);
+       let bob_id = bob.node.get_our_node_id();
+
+       disconnect_peers(alice, &[charlie, david, &nodes[4], &nodes[5]]);
+       disconnect_peers(david, &[bob, &nodes[4], &nodes[5]]);
+
+       let offer = bob.node
+               .create_offer_builder("coffee".to_string()).unwrap()
+               .amount_msats(10_000_000)
+               .build().unwrap();
+       assert_ne!(offer.signing_pubkey(), bob_id);
+       assert!(!offer.paths().is_empty());
+       for path in offer.paths() {
+               assert_eq!(path.introduction_node_id, nodes[4].node.get_our_node_id());
+       }
+}
+
 /// Checks that an offer can be paid through blinded paths and that ephemeral pubkeys are used
 /// rather than exposing a node's pubkey.
 #[test]
@@ -582,6 +733,61 @@ fn fails_creating_refund_without_blinded_paths() {
        assert!(nodes[0].node.list_recent_payments().is_empty());
 }
 
+/// Fails creating an invoice request when the offer contains an unsupported chain.
+#[test]
+fn fails_creating_invoice_request_for_unsupported_chain() {
+       let chanmon_cfgs = create_chanmon_cfgs(2);
+       let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+       let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+       create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
+
+       let alice = &nodes[0];
+       let bob = &nodes[1];
+
+       let offer = alice.node
+               .create_offer_builder("coffee".to_string()).unwrap()
+               .clear_chains()
+               .chain(Network::Signet)
+               .build().unwrap();
+
+       let payment_id = PaymentId([1; 32]);
+       match bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None) {
+               Ok(_) => panic!("Expected error"),
+               Err(e) => assert_eq!(e, Bolt12SemanticError::UnsupportedChain),
+       }
+}
+
+/// Fails requesting a payment when the refund contains an unsupported chain.
+#[test]
+fn fails_sending_invoice_with_unsupported_chain_for_refund() {
+       let chanmon_cfgs = create_chanmon_cfgs(2);
+       let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+       let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+       create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
+
+       let alice = &nodes[0];
+       let bob = &nodes[1];
+
+       let absolute_expiry = Duration::from_secs(u64::MAX);
+       let payment_id = PaymentId([1; 32]);
+       let refund = bob.node
+               .create_refund_builder(
+                       "refund".to_string(), 10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
+               )
+               .unwrap()
+               .chain(Network::Signet)
+               .build().unwrap();
+
+       match alice.node.request_refund_payment(&refund) {
+               Ok(_) => panic!("Expected error"),
+               Err(e) => assert_eq!(e, Bolt12SemanticError::UnsupportedChain),
+       }
+}
+
 /// Fails creating an invoice request when a blinded reply path cannot be created without exposing
 /// the node's id.
 #[test]
index cfdba07a2f2839af321bddfc1e9f0c5ec26abc63..4bdd234f5aeecb214b24fa73b94d145004625c81 100644 (file)
@@ -1317,7 +1317,7 @@ fn test_phantom_failure_too_low_cltv() {
        // Ensure the payment fails with the expected error.
        let mut error_data = recv_value_msat.to_be_bytes().to_vec();
        error_data.extend_from_slice(
-               &nodes[0].node.best_block.read().unwrap().height().to_be_bytes(),
+               &nodes[0].node.best_block.read().unwrap().height.to_be_bytes(),
        );
        let mut fail_conditions = PaymentFailedConditions::new()
                .blamed_scid(phantom_scid)
@@ -1447,7 +1447,7 @@ fn test_phantom_failure_too_low_recv_amt() {
 
        // Ensure the payment fails with the expected error.
        let mut error_data = bad_recv_amt_msat.to_be_bytes().to_vec();
-       error_data.extend_from_slice(&nodes[1].node.best_block.read().unwrap().height().to_be_bytes());
+       error_data.extend_from_slice(&nodes[1].node.best_block.read().unwrap().height.to_be_bytes());
        let mut fail_conditions = PaymentFailedConditions::new()
                .blamed_scid(phantom_scid)
                .expected_htlc_error_data(0x4000 | 15, &error_data);
@@ -1554,7 +1554,7 @@ fn test_phantom_failure_reject_payment() {
 
        // Ensure the payment fails with the expected error.
        let mut error_data = recv_amt_msat.to_be_bytes().to_vec();
-       error_data.extend_from_slice(&nodes[1].node.best_block.read().unwrap().height().to_be_bytes());
+       error_data.extend_from_slice(&nodes[1].node.best_block.read().unwrap().height.to_be_bytes());
        let mut fail_conditions = PaymentFailedConditions::new()
                .blamed_scid(phantom_scid)
                .expected_htlc_error_data(0x4000 | 15, &error_data);
index 76dd78ae2ad6a712560c5d6d6aa1a1c0b2e182e5..4422af1e8c70c63a81b93ece1aac38e2553d00a0 100644 (file)
@@ -823,8 +823,12 @@ fn do_test_partial_claim_before_restart(persist_both_monitors: bool) {
        assert_eq!(send_events.len(), 2);
        let node_1_msgs = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut send_events);
        let node_2_msgs = remove_first_msg_event_to_node(&nodes[2].node.get_our_node_id(), &mut send_events);
-       do_pass_along_path(&nodes[0], &[&nodes[1], &nodes[3]], 15_000_000, payment_hash, Some(payment_secret), node_1_msgs, true, false, None, false);
-       do_pass_along_path(&nodes[0], &[&nodes[2], &nodes[3]], 15_000_000, payment_hash, Some(payment_secret), node_2_msgs, true, false, None, false);
+       do_pass_along_path(PassAlongPathArgs::new(&nodes[0],&[&nodes[1], &nodes[3]], 15_000_000, payment_hash, node_1_msgs)
+               .with_payment_secret(payment_secret)
+               .without_clearing_recipient_events());
+       do_pass_along_path(PassAlongPathArgs::new(&nodes[0], &[&nodes[2], &nodes[3]], 15_000_000, payment_hash, node_2_msgs)
+               .with_payment_secret(payment_secret)
+               .without_clearing_recipient_events());
 
        // Now that we have an MPP payment pending, get the latest encoded copies of nodes[3]'s
        // monitors and ChannelManager, for use later, if we don't want to persist both monitors.
index ee6a6afb6ecda2ae2a919a610b9ba9e978d5e5e9..c6d0cc58b9bf299a1fc2d6e06a34a9a6bc0b3958 100644 (file)
@@ -437,7 +437,7 @@ fn do_htlc_fail_async_shutdown(blinded_recipient: bool) {
        let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), None);
        let route_params = if blinded_recipient {
                crate::ln::blinded_payment_tests::get_blinded_route_parameters(
-                       amt_msat, our_payment_secret,
+                       amt_msat, our_payment_secret, 1, 100000000,
                        nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_2.0.contents],
                        &chanmon_cfgs[2].keys_manager)
        } else {
index bb29c76164e1aa7422c5dddda07b8cbd3e7ac01f..fc8371023484d8ade7a2708340a0e2402685d710 100644 (file)
 //!
 //! use bitcoin::hashes::Hash;
 //! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
-//! use core::convert::{Infallible, TryFrom};
+//! use core::convert::TryFrom;
+//! use lightning::offers::invoice::UnsignedBolt12Invoice;
 //! use lightning::offers::invoice_request::InvoiceRequest;
 //! use lightning::offers::refund::Refund;
 //! use lightning::util::ser::Writeable;
 //!
 //! # use lightning::ln::PaymentHash;
-//! # use lightning::offers::invoice::BlindedPayInfo;
+//! # use lightning::offers::invoice::{BlindedPayInfo, ExplicitSigningPubkey, InvoiceBuilder};
 //! # use lightning::blinded_path::BlindedPath;
 //! #
 //! # fn create_payment_paths() -> Vec<(BlindedPayInfo, BlindedPath)> { unimplemented!() }
@@ -44,6 +45,7 @@
 //! let mut buffer = Vec::new();
 //!
 //! // Invoice for the "offer to be paid" flow.
+//! # <InvoiceBuilder<ExplicitSigningPubkey>>::from(
 //! InvoiceRequest::try_from(bytes)?
 #![cfg_attr(feature = "std", doc = "
     .respond_with(payment_paths, payment_hash)?
 #![cfg_attr(not(feature = "std"), doc = "
     .respond_with_no_std(payment_paths, payment_hash, core::time::Duration::from_secs(0))?
 ")]
+//! # )
 //!     .relative_expiry(3600)
 //!     .allow_mpp()
 //!     .fallback_v0_p2wpkh(&wpubkey_hash)
 //!     .build()?
-//!     .sign::<_, Infallible>(
-//!         |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+//!     .sign(|message: &UnsignedBolt12Invoice|
+//!         Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
 //!     )
 //!     .expect("failed verifying signature")
 //!     .write(&mut buffer)
@@ -74,6 +77,7 @@
 //! # let mut buffer = Vec::new();
 //!
 //! // Invoice for the "offer for money" flow.
+//! # <InvoiceBuilder<ExplicitSigningPubkey>>::from(
 //! "lnr1qcp4256ypq"
 //!     .parse::<Refund>()?
 #![cfg_attr(feature = "std", doc = "
 #![cfg_attr(not(feature = "std"), doc = "
     .respond_with_no_std(payment_paths, payment_hash, pubkey, core::time::Duration::from_secs(0))?
 ")]
+//! # )
 //!     .relative_expiry(3600)
 //!     .allow_mpp()
 //!     .fallback_v0_p2wpkh(&wpubkey_hash)
 //!     .build()?
-//!     .sign::<_, Infallible>(
-//!         |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+//!     .sign(|message: &UnsignedBolt12Invoice|
+//!         Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
 //!     )
 //!     .expect("failed verifying signature")
 //!     .write(&mut buffer)
@@ -105,7 +110,7 @@ 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::convert::{AsRef, Infallible, TryFrom};
+use core::convert::{AsRef, TryFrom};
 use core::time::Duration;
 use crate::io;
 use crate::blinded_path::BlindedPath;
@@ -115,7 +120,7 @@ use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures, InvoiceRequ
 use crate::ln::inbound_payment::ExpandedKey;
 use crate::ln::msgs::DecodeError;
 use crate::offers::invoice_request::{INVOICE_REQUEST_PAYER_ID_TYPE, INVOICE_REQUEST_TYPES, IV_BYTES as INVOICE_REQUEST_IV_BYTES, InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
-use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, WithoutSignatures, self};
+use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, WithoutSignatures, self};
 use crate::offers::offer::{Amount, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
 use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
 use crate::offers::payer::{PAYER_METADATA_TYPE, PayerTlvStream, PayerTlvStreamRef};
@@ -151,6 +156,38 @@ pub struct InvoiceBuilder<'a, S: SigningPubkeyStrategy> {
        signing_pubkey_strategy: S,
 }
 
+/// Builds a [`Bolt12Invoice`] from either:
+/// - an [`InvoiceRequest`] for the "offer to be paid" flow or
+/// - a [`Refund`] for the "offer for money" flow.
+///
+/// See [module-level documentation] for usage.
+///
+/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+/// [`Refund`]: crate::offers::refund::Refund
+/// [module-level documentation]: self
+#[cfg(c_bindings)]
+pub struct InvoiceWithExplicitSigningPubkeyBuilder<'a> {
+       invreq_bytes: &'a Vec<u8>,
+       invoice: InvoiceContents,
+       signing_pubkey_strategy: ExplicitSigningPubkey,
+}
+
+/// Builds a [`Bolt12Invoice`] from either:
+/// - an [`InvoiceRequest`] for the "offer to be paid" flow or
+/// - a [`Refund`] for the "offer for money" flow.
+///
+/// See [module-level documentation] for usage.
+///
+/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+/// [`Refund`]: crate::offers::refund::Refund
+/// [module-level documentation]: self
+#[cfg(c_bindings)]
+pub struct InvoiceWithDerivedSigningPubkeyBuilder<'a> {
+       invreq_bytes: &'a Vec<u8>,
+       invoice: InvoiceContents,
+       signing_pubkey_strategy: DerivedSigningPubkey,
+}
+
 /// Indicates how [`Bolt12Invoice::signing_pubkey`] was set.
 ///
 /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
@@ -169,7 +206,8 @@ pub struct DerivedSigningPubkey(KeyPair);
 impl SigningPubkeyStrategy for ExplicitSigningPubkey {}
 impl SigningPubkeyStrategy for DerivedSigningPubkey {}
 
-impl<'a> InvoiceBuilder<'a, ExplicitSigningPubkey> {
+macro_rules! invoice_explicit_signing_pubkey_builder_methods { ($self: ident, $self_type: ty) => {
+       #[cfg_attr(c_bindings, allow(dead_code))]
        pub(super) fn for_offer(
                invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
                created_at: Duration, payment_hash: PaymentHash
@@ -186,6 +224,7 @@ impl<'a> InvoiceBuilder<'a, ExplicitSigningPubkey> {
                Self::new(&invoice_request.bytes, contents, ExplicitSigningPubkey {})
        }
 
+       #[cfg_attr(c_bindings, allow(dead_code))]
        pub(super) fn for_refund(
                refund: &'a Refund, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, created_at: Duration,
                payment_hash: PaymentHash, signing_pubkey: PublicKey
@@ -200,9 +239,34 @@ impl<'a> InvoiceBuilder<'a, ExplicitSigningPubkey> {
 
                Self::new(&refund.bytes, contents, ExplicitSigningPubkey {})
        }
-}
 
-impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
+       /// Builds an unsigned [`Bolt12Invoice`] after checking for valid semantics. It can be signed by
+       /// [`UnsignedBolt12Invoice::sign`].
+       pub fn build($self: $self_type) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
+               #[cfg(feature = "std")] {
+                       if $self.invoice.is_offer_or_refund_expired() {
+                               return Err(Bolt12SemanticError::AlreadyExpired);
+                       }
+               }
+
+               #[cfg(not(feature = "std"))] {
+                       if $self.invoice.is_offer_or_refund_expired_no_std($self.invoice.created_at()) {
+                               return Err(Bolt12SemanticError::AlreadyExpired);
+                       }
+               }
+
+               let Self { invreq_bytes, invoice, .. } = $self;
+               #[cfg(not(c_bindings))] {
+                       Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice))
+               }
+               #[cfg(c_bindings)] {
+                       Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice.clone()))
+               }
+       }
+} }
+
+macro_rules! invoice_derived_signing_pubkey_builder_methods { ($self: ident, $self_type: ty) => {
+       #[cfg_attr(c_bindings, allow(dead_code))]
        pub(super) fn for_offer_using_keys(
                invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
                created_at: Duration, payment_hash: PaymentHash, keys: KeyPair
@@ -219,6 +283,7 @@ impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
                Self::new(&invoice_request.bytes, contents, DerivedSigningPubkey(keys))
        }
 
+       #[cfg_attr(c_bindings, allow(dead_code))]
        pub(super) fn for_refund_using_keys(
                refund: &'a Refund, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, created_at: Duration,
                payment_hash: PaymentHash, keys: KeyPair,
@@ -234,9 +299,43 @@ impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
 
                Self::new(&refund.bytes, contents, DerivedSigningPubkey(keys))
        }
-}
 
-impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> {
+       /// Builds a signed [`Bolt12Invoice`] after checking for valid semantics.
+       pub fn build_and_sign<T: secp256k1::Signing>(
+               $self: $self_type, secp_ctx: &Secp256k1<T>
+       ) -> Result<Bolt12Invoice, Bolt12SemanticError> {
+               #[cfg(feature = "std")] {
+                       if $self.invoice.is_offer_or_refund_expired() {
+                               return Err(Bolt12SemanticError::AlreadyExpired);
+                       }
+               }
+
+               #[cfg(not(feature = "std"))] {
+                       if $self.invoice.is_offer_or_refund_expired_no_std($self.invoice.created_at()) {
+                               return Err(Bolt12SemanticError::AlreadyExpired);
+                       }
+               }
+
+               let Self {
+                       invreq_bytes, invoice, signing_pubkey_strategy: DerivedSigningPubkey(keys)
+               } = $self;
+               #[cfg(not(c_bindings))]
+               let unsigned_invoice = UnsignedBolt12Invoice::new(invreq_bytes, invoice);
+               #[cfg(c_bindings)]
+               let mut unsigned_invoice = UnsignedBolt12Invoice::new(invreq_bytes, invoice.clone());
+
+               let invoice = unsigned_invoice
+                       .sign(|message: &UnsignedBolt12Invoice|
+                               Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+                       )
+                       .unwrap();
+               Ok(invoice)
+       }
+} }
+
+macro_rules! invoice_builder_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr, $type_param: ty $(, $self_mut: tt)?
+) => {
        pub(crate) fn amount_msats(
                invoice_request: &InvoiceRequest
        ) -> Result<u64, Bolt12SemanticError> {
@@ -253,6 +352,7 @@ impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> {
                }
        }
 
+       #[cfg_attr(c_bindings, allow(dead_code))]
        fn fields(
                payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, created_at: Duration,
                payment_hash: PaymentHash, amount_msats: u64, signing_pubkey: PublicKey
@@ -263,8 +363,9 @@ impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> {
                }
        }
 
+       #[cfg_attr(c_bindings, allow(dead_code))]
        fn new(
-               invreq_bytes: &'a Vec<u8>, contents: InvoiceContents, signing_pubkey_strategy: S
+               invreq_bytes: &'a Vec<u8>, contents: InvoiceContents, signing_pubkey_strategy: $type_param
        ) -> Result<Self, Bolt12SemanticError> {
                if contents.fields().payment_paths.is_empty() {
                        return Err(Bolt12SemanticError::MissingPaths);
@@ -278,108 +379,120 @@ impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> {
        /// [`Bolt12Invoice::is_expired`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn relative_expiry(mut self, relative_expiry_secs: u32) -> Self {
+       pub fn relative_expiry($($self_mut)* $self: $self_type, relative_expiry_secs: u32) -> $return_type {
                let relative_expiry = Duration::from_secs(relative_expiry_secs as u64);
-               self.invoice.fields_mut().relative_expiry = Some(relative_expiry);
-               self
+               $self.invoice.fields_mut().relative_expiry = Some(relative_expiry);
+               $return_value
        }
 
        /// Adds a P2WSH address to [`Bolt12Invoice::fallbacks`].
        ///
        /// 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(mut self, script_hash: &WScriptHash) -> Self {
+       pub fn fallback_v0_p2wsh($($self_mut)* $self: $self_type, script_hash: &WScriptHash) -> $return_type {
                let address = FallbackAddress {
                        version: WitnessVersion::V0.to_num(),
                        program: Vec::from(script_hash.to_byte_array()),
                };
-               self.invoice.fields_mut().fallbacks.get_or_insert_with(Vec::new).push(address);
-               self
+               $self.invoice.fields_mut().fallbacks.get_or_insert_with(Vec::new).push(address);
+               $return_value
        }
 
        /// Adds a P2WPKH address to [`Bolt12Invoice::fallbacks`].
        ///
        /// 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(mut self, pubkey_hash: &WPubkeyHash) -> Self {
+       pub fn fallback_v0_p2wpkh($($self_mut)* $self: $self_type, pubkey_hash: &WPubkeyHash) -> $return_type {
                let address = FallbackAddress {
                        version: WitnessVersion::V0.to_num(),
                        program: Vec::from(pubkey_hash.to_byte_array()),
                };
-               self.invoice.fields_mut().fallbacks.get_or_insert_with(Vec::new).push(address);
-               self
+               $self.invoice.fields_mut().fallbacks.get_or_insert_with(Vec::new).push(address);
+               $return_value
        }
 
        /// Adds a P2TR address to [`Bolt12Invoice::fallbacks`].
        ///
        /// 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 P2TR addresses.
-       pub fn fallback_v1_p2tr_tweaked(mut self, output_key: &TweakedPublicKey) -> Self {
+       pub fn fallback_v1_p2tr_tweaked($($self_mut)* $self: $self_type, output_key: &TweakedPublicKey) -> $return_type {
                let address = FallbackAddress {
                        version: WitnessVersion::V1.to_num(),
                        program: Vec::from(&output_key.serialize()[..]),
                };
-               self.invoice.fields_mut().fallbacks.get_or_insert_with(Vec::new).push(address);
-               self
+               $self.invoice.fields_mut().fallbacks.get_or_insert_with(Vec::new).push(address);
+               $return_value
        }
 
        /// Sets [`Bolt12Invoice::invoice_features`] to indicate MPP may be used. Otherwise, MPP is
        /// disallowed.
-       pub fn allow_mpp(mut self) -> Self {
-               self.invoice.fields_mut().features.set_basic_mpp_optional();
-               self
+       pub fn allow_mpp($($self_mut)* $self: $self_type) -> $return_type {
+               $self.invoice.fields_mut().features.set_basic_mpp_optional();
+               $return_value
        }
-}
+} }
 
 impl<'a> InvoiceBuilder<'a, ExplicitSigningPubkey> {
-       /// Builds an unsigned [`Bolt12Invoice`] after checking for valid semantics. It can be signed by
-       /// [`UnsignedBolt12Invoice::sign`].
-       pub fn build(self) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
-               #[cfg(feature = "std")] {
-                       if self.invoice.is_offer_or_refund_expired() {
-                               return Err(Bolt12SemanticError::AlreadyExpired);
-                       }
-               }
+       invoice_explicit_signing_pubkey_builder_methods!(self, Self);
+}
 
-               #[cfg(not(feature = "std"))] {
-                       if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) {
-                               return Err(Bolt12SemanticError::AlreadyExpired);
-                       }
-               }
+impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
+       invoice_derived_signing_pubkey_builder_methods!(self, Self);
+}
 
-               let InvoiceBuilder { invreq_bytes, invoice, .. } = self;
-               Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice))
-       }
+impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> {
+       invoice_builder_methods!(self, Self, Self, self, S, mut);
 }
 
-impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
-       /// Builds a signed [`Bolt12Invoice`] after checking for valid semantics.
-       pub fn build_and_sign<T: secp256k1::Signing>(
-               self, secp_ctx: &Secp256k1<T>
-       ) -> Result<Bolt12Invoice, Bolt12SemanticError> {
-               #[cfg(feature = "std")] {
-                       if self.invoice.is_offer_or_refund_expired() {
-                               return Err(Bolt12SemanticError::AlreadyExpired);
-                       }
-               }
+#[cfg(all(c_bindings, not(test)))]
+impl<'a> InvoiceWithExplicitSigningPubkeyBuilder<'a> {
+       invoice_explicit_signing_pubkey_builder_methods!(self, &mut Self);
+       invoice_builder_methods!(self, &mut Self, (), (), ExplicitSigningPubkey);
+}
 
-               #[cfg(not(feature = "std"))] {
-                       if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) {
-                               return Err(Bolt12SemanticError::AlreadyExpired);
-                       }
+#[cfg(all(c_bindings, test))]
+impl<'a> InvoiceWithExplicitSigningPubkeyBuilder<'a> {
+       invoice_explicit_signing_pubkey_builder_methods!(self, &mut Self);
+       invoice_builder_methods!(self, &mut Self, &mut Self, self, ExplicitSigningPubkey);
+}
+
+#[cfg(all(c_bindings, not(test)))]
+impl<'a> InvoiceWithDerivedSigningPubkeyBuilder<'a> {
+       invoice_derived_signing_pubkey_builder_methods!(self, &mut Self);
+       invoice_builder_methods!(self, &mut Self, (), (), DerivedSigningPubkey);
+}
+
+#[cfg(all(c_bindings, test))]
+impl<'a> InvoiceWithDerivedSigningPubkeyBuilder<'a> {
+       invoice_derived_signing_pubkey_builder_methods!(self, &mut Self);
+       invoice_builder_methods!(self, &mut Self, &mut Self, self, DerivedSigningPubkey);
+}
+
+#[cfg(c_bindings)]
+impl<'a> From<InvoiceWithExplicitSigningPubkeyBuilder<'a>>
+for InvoiceBuilder<'a, ExplicitSigningPubkey> {
+       fn from(builder: InvoiceWithExplicitSigningPubkeyBuilder<'a>) -> Self {
+               let InvoiceWithExplicitSigningPubkeyBuilder {
+                       invreq_bytes, invoice, signing_pubkey_strategy,
+               } = builder;
+
+               Self {
+                       invreq_bytes, invoice, signing_pubkey_strategy,
                }
+       }
+}
 
-               let InvoiceBuilder {
-                       invreq_bytes, invoice, signing_pubkey_strategy: DerivedSigningPubkey(keys)
-               } = self;
-               let unsigned_invoice = UnsignedBolt12Invoice::new(invreq_bytes, invoice);
+#[cfg(c_bindings)]
+impl<'a> From<InvoiceWithDerivedSigningPubkeyBuilder<'a>>
+for InvoiceBuilder<'a, DerivedSigningPubkey> {
+       fn from(builder: InvoiceWithDerivedSigningPubkeyBuilder<'a>) -> Self {
+               let InvoiceWithDerivedSigningPubkeyBuilder {
+                       invreq_bytes, invoice, signing_pubkey_strategy,
+               } = builder;
 
-               let invoice = unsigned_invoice
-                       .sign::<_, Infallible>(
-                               |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
-                       )
-                       .unwrap();
-               Ok(invoice)
+               Self {
+                       invreq_bytes, invoice, signing_pubkey_strategy,
+               }
        }
 }
 
@@ -395,6 +508,30 @@ pub struct UnsignedBolt12Invoice {
        tagged_hash: TaggedHash,
 }
 
+/// A function for signing an [`UnsignedBolt12Invoice`].
+pub trait SignBolt12InvoiceFn {
+       /// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream.
+       fn sign_invoice(&self, message: &UnsignedBolt12Invoice) -> Result<Signature, ()>;
+}
+
+impl<F> SignBolt12InvoiceFn for F
+where
+       F: Fn(&UnsignedBolt12Invoice) -> Result<Signature, ()>,
+{
+       fn sign_invoice(&self, message: &UnsignedBolt12Invoice) -> Result<Signature, ()> {
+               self(message)
+       }
+}
+
+impl<F> SignFn<UnsignedBolt12Invoice> for F
+where
+       F: SignBolt12InvoiceFn,
+{
+       fn sign(&self, message: &UnsignedBolt12Invoice) -> Result<Signature, ()> {
+               self.sign_invoice(message)
+       }
+}
+
 impl UnsignedBolt12Invoice {
        fn new(invreq_bytes: &[u8], contents: InvoiceContents) -> Self {
                // Use the invoice_request bytes instead of the invoice_request TLV stream as the latter may
@@ -416,32 +553,50 @@ impl UnsignedBolt12Invoice {
        pub fn tagged_hash(&self) -> &TaggedHash {
                &self.tagged_hash
        }
+}
 
+macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $self_mut: tt)?) => {
        /// Signs the [`TaggedHash`] of the invoice using the given function.
        ///
        /// Note: The hash computation may have included unknown, odd TLV records.
-       ///
-       /// This is not exported to bindings users as functions aren't currently mapped.
-       pub fn sign<F, E>(mut self, sign: F) -> Result<Bolt12Invoice, SignError<E>>
-       where
-               F: FnOnce(&Self) -> Result<Signature, E>
-       {
-               let pubkey = self.contents.fields().signing_pubkey;
-               let signature = merkle::sign_message(sign, &self, pubkey)?;
+       pub fn sign<F: SignBolt12InvoiceFn>(
+               $($self_mut)* $self: $self_type, sign: F
+       ) -> Result<Bolt12Invoice, SignError> {
+               let pubkey = $self.contents.fields().signing_pubkey;
+               let signature = merkle::sign_message(sign, &$self, pubkey)?;
 
                // Append the signature TLV record to the bytes.
                let signature_tlv_stream = SignatureTlvStreamRef {
                        signature: Some(&signature),
                };
-               signature_tlv_stream.write(&mut self.bytes).unwrap();
+               signature_tlv_stream.write(&mut $self.bytes).unwrap();
 
                Ok(Bolt12Invoice {
-                       bytes: self.bytes,
-                       contents: self.contents,
+                       #[cfg(not(c_bindings))]
+                       bytes: $self.bytes,
+                       #[cfg(c_bindings)]
+                       bytes: $self.bytes.clone(),
+                       #[cfg(not(c_bindings))]
+                       contents: $self.contents,
+                       #[cfg(c_bindings)]
+                       contents: $self.contents.clone(),
                        signature,
-                       tagged_hash: self.tagged_hash,
+                       #[cfg(not(c_bindings))]
+                       tagged_hash: $self.tagged_hash,
+                       #[cfg(c_bindings)]
+                       tagged_hash: $self.tagged_hash.clone(),
                })
        }
+} }
+
+#[cfg(not(c_bindings))]
+impl UnsignedBolt12Invoice {
+       unsigned_invoice_sign_method!(self, Self, mut);
+}
+
+#[cfg(c_bindings)]
+impl UnsignedBolt12Invoice {
+       unsigned_invoice_sign_method!(self, &mut Self);
 }
 
 impl AsRef<TaggedHash> for UnsignedBolt12Invoice {
@@ -1306,10 +1461,19 @@ mod tests {
        use crate::ln::msgs::DecodeError;
        use crate::offers::invoice_request::InvoiceRequestTlvStreamRef;
        use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
-       use crate::offers::offer::{Amount, OfferBuilder, OfferTlvStreamRef, Quantity};
+       use crate::offers::offer::{Amount, OfferTlvStreamRef, Quantity};
+       #[cfg(not(c_bindings))]
+       use {
+               crate::offers::offer::OfferBuilder,
+               crate::offers::refund::RefundBuilder,
+       };
+       #[cfg(c_bindings)]
+       use {
+               crate::offers::offer::OfferWithExplicitMetadataBuilder as OfferBuilder,
+               crate::offers::refund::RefundMaybeWithDerivedMetadataBuilder as RefundBuilder,
+       };
        use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
        use crate::offers::payer::PayerTlvStreamRef;
-       use crate::offers::refund::RefundBuilder;
        use crate::offers::test_utils::*;
        use crate::util::ser::{BigSize, Iterable, Writeable};
        use crate::util::string::PrintableString;
@@ -1384,6 +1548,8 @@ mod tests {
                        },
                }
 
+               #[cfg(c_bindings)]
+               let mut unsigned_invoice = unsigned_invoice;
                let invoice = unsigned_invoice.sign(recipient_sign).unwrap();
 
                let mut buffer = Vec::new();
@@ -1644,6 +1810,8 @@ mod tests {
                        ],
                };
 
+               #[cfg(c_bindings)]
+               use crate::offers::offer::OfferWithDerivedMetadataBuilder as OfferBuilder;
                let offer = OfferBuilder
                        ::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
                        .amount_msats(1000)
@@ -1867,10 +2035,10 @@ mod tests {
                        .sign(payer_sign).unwrap()
                        .respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
                        .build().unwrap()
-                       .sign(|_| Err(()))
+                       .sign(fail_sign)
                {
                        Ok(_) => panic!("expected error"),
-                       Err(e) => assert_eq!(e, SignError::Signing(())),
+                       Err(e) => assert_eq!(e, SignError::Signing),
                }
 
                match OfferBuilder::new("foo".into(), recipient_pubkey())
@@ -2093,11 +2261,18 @@ mod tests {
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
                        .build().unwrap()
                        .sign(payer_sign).unwrap();
+               #[cfg(not(c_bindings))]
+               let invoice_builder = invoice_request
+                       .respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap();
+               #[cfg(c_bindings)]
                let mut invoice_builder = invoice_request
-                       .respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
+                       .respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap();
+               let invoice_builder = invoice_builder
                        .fallback_v0_p2wsh(&script.wscript_hash())
                        .fallback_v0_p2wpkh(&pubkey.wpubkey_hash().unwrap())
                        .fallback_v1_p2tr_tweaked(&tweaked_pubkey);
+               #[cfg(not(c_bindings))]
+               let mut invoice_builder = invoice_builder;
 
                // Only standard addresses will be included.
                let fallbacks = invoice_builder.invoice.fields_mut().fallbacks.as_mut().unwrap();
index 4dd85b352f708de598c60c89e91a3b1896ec81a4..f282075d9333cf1f9002154519474518036b9336 100644 (file)
@@ -25,8 +25,8 @@
 //!
 //! use bitcoin::network::constants::Network;
 //! use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
-//! use core::convert::Infallible;
 //! use lightning::ln::features::OfferFeatures;
+//! use lightning::offers::invoice_request::UnsignedInvoiceRequest;
 //! use lightning::offers::offer::Offer;
 //! use lightning::util::ser::Writeable;
 //!
 //! let pubkey = PublicKey::from(keys);
 //! let mut buffer = Vec::new();
 //!
+//! # use lightning::offers::invoice_request::{ExplicitPayerId, InvoiceRequestBuilder};
+//! # <InvoiceRequestBuilder<ExplicitPayerId, _>>::from(
 //! "lno1qcp4256ypq"
 //!     .parse::<Offer>()?
 //!     .request_invoice(vec![42; 64], pubkey)?
+//! # )
 //!     .chain(Network::Testnet)?
 //!     .amount_msats(1000)?
 //!     .quantity(5)?
 //!     .payer_note("foo".to_string())
 //!     .build()?
-//!     .sign::<_, Infallible>(
-//!         |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+//!     .sign(|message: &UnsignedInvoiceRequest|
+//!         Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
 //!     )
 //!     .expect("failed verifying signature")
 //!     .write(&mut buffer)
@@ -58,7 +61,7 @@ use bitcoin::blockdata::constants::ChainHash;
 use bitcoin::network::constants::Network;
 use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, self};
 use bitcoin::secp256k1::schnorr::Signature;
-use core::convert::{AsRef, Infallible, TryFrom};
+use core::convert::{AsRef, TryFrom};
 use core::ops::Deref;
 use crate::sign::EntropySource;
 use crate::io;
@@ -68,8 +71,8 @@ use crate::ln::channelmanager::PaymentId;
 use crate::ln::features::InvoiceRequestFeatures;
 use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
 use crate::ln::msgs::DecodeError;
-use crate::offers::invoice::{BlindedPayInfo, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder};
-use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, self};
+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::parse::{Bolt12ParseError, ParsedMessage, Bolt12SemanticError};
 use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
@@ -77,6 +80,15 @@ use crate::offers::signer::{Metadata, MetadataMaterial};
 use crate::util::ser::{HighZeroBytesDroppedBigSize, SeekReadable, WithoutLength, Writeable, Writer};
 use crate::util::string::PrintableString;
 
+#[cfg(not(c_bindings))]
+use {
+       crate::offers::invoice::{DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder},
+};
+#[cfg(c_bindings)]
+use {
+       crate::offers::invoice::{InvoiceWithDerivedSigningPubkeyBuilder, InvoiceWithExplicitSigningPubkeyBuilder},
+};
+
 use crate::prelude::*;
 
 /// Tag for the hash function used when signing an [`InvoiceRequest`]'s merkle root.
@@ -99,6 +111,34 @@ pub struct InvoiceRequestBuilder<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signi
        secp_ctx: Option<&'b Secp256k1<T>>,
 }
 
+/// Builds an [`InvoiceRequest`] from an [`Offer`] for the "offer to be paid" flow.
+///
+/// See [module-level documentation] for usage.
+///
+/// [module-level documentation]: self
+#[cfg(c_bindings)]
+pub struct InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b> {
+       offer: &'a Offer,
+       invoice_request: InvoiceRequestContentsWithoutPayerId,
+       payer_id: Option<PublicKey>,
+       payer_id_strategy: core::marker::PhantomData<ExplicitPayerId>,
+       secp_ctx: Option<&'b Secp256k1<secp256k1::All>>,
+}
+
+/// Builds an [`InvoiceRequest`] from an [`Offer`] for the "offer to be paid" flow.
+///
+/// See [module-level documentation] for usage.
+///
+/// [module-level documentation]: self
+#[cfg(c_bindings)]
+pub struct InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b> {
+       offer: &'a Offer,
+       invoice_request: InvoiceRequestContentsWithoutPayerId,
+       payer_id: Option<PublicKey>,
+       payer_id_strategy: core::marker::PhantomData<DerivedPayerId>,
+       secp_ctx: Option<&'b Secp256k1<secp256k1::All>>,
+}
+
 /// Indicates how [`InvoiceRequest::payer_id`] will be set.
 ///
 /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
@@ -117,7 +157,8 @@ pub struct DerivedPayerId {}
 impl PayerIdStrategy for ExplicitPayerId {}
 impl PayerIdStrategy for DerivedPayerId {}
 
-impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, ExplicitPayerId, T> {
+macro_rules! invoice_request_explicit_payer_id_builder_methods { ($self: ident, $self_type: ty) => {
+       #[cfg_attr(c_bindings, allow(dead_code))]
        pub(super) fn new(offer: &'a Offer, metadata: Vec<u8>, payer_id: PublicKey) -> Self {
                Self {
                        offer,
@@ -128,6 +169,7 @@ impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, ExplicitPayerI
                }
        }
 
+       #[cfg_attr(c_bindings, allow(dead_code))]
        pub(super) fn deriving_metadata<ES: Deref>(
                offer: &'a Offer, payer_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
                payment_id: PaymentId,
@@ -144,12 +186,23 @@ impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, ExplicitPayerI
                        secp_ctx: None,
                }
        }
-}
 
-impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T> {
+       /// Builds an unsigned [`InvoiceRequest`] after checking for valid semantics. It can be signed
+       /// by [`UnsignedInvoiceRequest::sign`].
+       pub fn build($self: $self_type) -> Result<UnsignedInvoiceRequest, Bolt12SemanticError> {
+               let (unsigned_invoice_request, keys, _) = $self.build_with_checks()?;
+               debug_assert!(keys.is_none());
+               Ok(unsigned_invoice_request)
+       }
+} }
+
+macro_rules! invoice_request_derived_payer_id_builder_methods { (
+       $self: ident, $self_type: ty, $secp_context: ty
+) => {
+       #[cfg_attr(c_bindings, allow(dead_code))]
        pub(super) fn deriving_payer_id<ES: Deref>(
                offer: &'a Offer, expanded_key: &ExpandedKey, entropy_source: ES,
-               secp_ctx: &'b Secp256k1<T>, payment_id: PaymentId
+               secp_ctx: &'b Secp256k1<$secp_context>, payment_id: PaymentId
        ) -> Self where ES::Target: EntropySource {
                let nonce = Nonce::from_entropy_source(entropy_source);
                let payment_id = Some(payment_id);
@@ -163,9 +216,29 @@ impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, DerivedPayerId
                        secp_ctx: Some(secp_ctx),
                }
        }
-}
 
-impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, P, T> {
+       /// Builds a signed [`InvoiceRequest`] after checking for valid semantics.
+       pub fn build_and_sign($self: $self_type) -> Result<InvoiceRequest, Bolt12SemanticError> {
+               let (unsigned_invoice_request, keys, secp_ctx) = $self.build_with_checks()?;
+               #[cfg(c_bindings)]
+               let mut unsigned_invoice_request = unsigned_invoice_request;
+               debug_assert!(keys.is_some());
+
+               let secp_ctx = secp_ctx.unwrap();
+               let keys = keys.unwrap();
+               let invoice_request = unsigned_invoice_request
+                       .sign(|message: &UnsignedInvoiceRequest|
+                               Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+                       )
+                       .unwrap();
+               Ok(invoice_request)
+       }
+} }
+
+macro_rules! invoice_request_builder_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr, $secp_context: ty $(, $self_mut: tt)?
+) => {
+       #[cfg_attr(c_bindings, allow(dead_code))]
        fn create_contents(offer: &Offer, metadata: Metadata) -> InvoiceRequestContentsWithoutPayerId {
                let offer = offer.contents.clone();
                InvoiceRequestContentsWithoutPayerId {
@@ -179,8 +252,8 @@ impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a
        /// by the offer.
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn chain(self, network: Network) -> Result<Self, Bolt12SemanticError> {
-               self.chain_hash(ChainHash::using_genesis_block(network))
+       pub fn chain($self: $self_type, network: Network) -> Result<$return_type, Bolt12SemanticError> {
+               $self.chain_hash(ChainHash::using_genesis_block(network))
        }
 
        /// Sets the [`InvoiceRequest::chain`] for paying an invoice. If not called, the chain hash of
@@ -188,13 +261,13 @@ impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a
        /// offer.
        ///
        /// Successive calls to this method will override the previous setting.
-       pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Result<Self, Bolt12SemanticError> {
-               if !self.offer.supports_chain(chain) {
+       pub(crate) fn chain_hash($($self_mut)* $self: $self_type, chain: ChainHash) -> Result<$return_type, Bolt12SemanticError> {
+               if !$self.offer.supports_chain(chain) {
                        return Err(Bolt12SemanticError::UnsupportedChain);
                }
 
-               self.invoice_request.chain = Some(chain);
-               Ok(self)
+               $self.invoice_request.chain = Some(chain);
+               Ok($return_value)
        }
 
        /// Sets the [`InvoiceRequest::amount_msats`] for paying an invoice. Errors if `amount_msats` is
@@ -203,156 +276,208 @@ impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a
        /// Successive calls to this method will override the previous setting.
        ///
        /// [`quantity`]: Self::quantity
-       pub fn amount_msats(mut self, amount_msats: u64) -> Result<Self, Bolt12SemanticError> {
-               self.invoice_request.offer.check_amount_msats_for_quantity(
-                       Some(amount_msats), self.invoice_request.quantity
+       pub fn amount_msats($($self_mut)* $self: $self_type, amount_msats: u64) -> Result<$return_type, Bolt12SemanticError> {
+               $self.invoice_request.offer.check_amount_msats_for_quantity(
+                       Some(amount_msats), $self.invoice_request.quantity
                )?;
-               self.invoice_request.amount_msats = Some(amount_msats);
-               Ok(self)
+               $self.invoice_request.amount_msats = Some(amount_msats);
+               Ok($return_value)
        }
 
        /// Sets [`InvoiceRequest::quantity`] of items. If not set, `1` is assumed. Errors if `quantity`
        /// does not conform to [`Offer::is_valid_quantity`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn quantity(mut self, quantity: u64) -> Result<Self, Bolt12SemanticError> {
-               self.invoice_request.offer.check_quantity(Some(quantity))?;
-               self.invoice_request.quantity = Some(quantity);
-               Ok(self)
+       pub fn quantity($($self_mut)* $self: $self_type, quantity: u64) -> Result<$return_type, Bolt12SemanticError> {
+               $self.invoice_request.offer.check_quantity(Some(quantity))?;
+               $self.invoice_request.quantity = Some(quantity);
+               Ok($return_value)
        }
 
        /// Sets the [`InvoiceRequest::payer_note`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn payer_note(mut self, payer_note: String) -> Self {
-               self.invoice_request.payer_note = Some(payer_note);
-               self
+       pub fn payer_note($($self_mut)* $self: $self_type, payer_note: String) -> $return_type {
+               $self.invoice_request.payer_note = Some(payer_note);
+               $return_value
        }
 
-       fn build_with_checks(mut self) -> Result<
-               (UnsignedInvoiceRequest, Option<KeyPair>, Option<&'b Secp256k1<T>>),
+       fn build_with_checks($($self_mut)* $self: $self_type) -> Result<
+               (UnsignedInvoiceRequest, Option<KeyPair>, Option<&'b Secp256k1<$secp_context>>),
                Bolt12SemanticError
        > {
                #[cfg(feature = "std")] {
-                       if self.offer.is_expired() {
+                       if $self.offer.is_expired() {
                                return Err(Bolt12SemanticError::AlreadyExpired);
                        }
                }
 
-               let chain = self.invoice_request.chain();
-               if !self.offer.supports_chain(chain) {
+               let chain = $self.invoice_request.chain();
+               if !$self.offer.supports_chain(chain) {
                        return Err(Bolt12SemanticError::UnsupportedChain);
                }
 
-               if chain == self.offer.implied_chain() {
-                       self.invoice_request.chain = None;
+               if chain == $self.offer.implied_chain() {
+                       $self.invoice_request.chain = None;
                }
 
-               if self.offer.amount().is_none() && self.invoice_request.amount_msats.is_none() {
+               if $self.offer.amount().is_none() && $self.invoice_request.amount_msats.is_none() {
                        return Err(Bolt12SemanticError::MissingAmount);
                }
 
-               self.invoice_request.offer.check_quantity(self.invoice_request.quantity)?;
-               self.invoice_request.offer.check_amount_msats_for_quantity(
-                       self.invoice_request.amount_msats, self.invoice_request.quantity
+               $self.invoice_request.offer.check_quantity($self.invoice_request.quantity)?;
+               $self.invoice_request.offer.check_amount_msats_for_quantity(
+                       $self.invoice_request.amount_msats, $self.invoice_request.quantity
                )?;
 
-               Ok(self.build_without_checks())
+               Ok($self.build_without_checks())
        }
 
-       fn build_without_checks(mut self) ->
-               (UnsignedInvoiceRequest, Option<KeyPair>, Option<&'b Secp256k1<T>>)
+       fn build_without_checks($($self_mut)* $self: $self_type) ->
+               (UnsignedInvoiceRequest, Option<KeyPair>, Option<&'b Secp256k1<$secp_context>>)
        {
                // Create the metadata for stateless verification of a Bolt12Invoice.
                let mut keys = None;
-               let secp_ctx = self.secp_ctx.clone();
-               if self.invoice_request.payer.0.has_derivation_material() {
-                       let mut metadata = core::mem::take(&mut self.invoice_request.payer.0);
+               let secp_ctx = $self.secp_ctx.clone();
+               if $self.invoice_request.payer.0.has_derivation_material() {
+                       let mut metadata = core::mem::take(&mut $self.invoice_request.payer.0);
 
-                       let mut tlv_stream = self.invoice_request.as_tlv_stream();
+                       let mut tlv_stream = $self.invoice_request.as_tlv_stream();
                        debug_assert!(tlv_stream.2.payer_id.is_none());
                        tlv_stream.0.metadata = None;
                        if !metadata.derives_payer_keys() {
-                               tlv_stream.2.payer_id = self.payer_id.as_ref();
+                               tlv_stream.2.payer_id = $self.payer_id.as_ref();
                        }
 
-                       let (derived_metadata, derived_keys) = metadata.derive_from(tlv_stream, self.secp_ctx);
+                       let (derived_metadata, derived_keys) = metadata.derive_from(tlv_stream, $self.secp_ctx);
                        metadata = derived_metadata;
                        keys = derived_keys;
                        if let Some(keys) = keys {
-                               debug_assert!(self.payer_id.is_none());
-                               self.payer_id = Some(keys.public_key());
+                               debug_assert!($self.payer_id.is_none());
+                               $self.payer_id = Some(keys.public_key());
                        }
 
-                       self.invoice_request.payer.0 = metadata;
+                       $self.invoice_request.payer.0 = metadata;
                }
 
-               debug_assert!(self.invoice_request.payer.0.as_bytes().is_some());
-               debug_assert!(self.payer_id.is_some());
-               let payer_id = self.payer_id.unwrap();
+               debug_assert!($self.invoice_request.payer.0.as_bytes().is_some());
+               debug_assert!($self.payer_id.is_some());
+               let payer_id = $self.payer_id.unwrap();
 
                let invoice_request = InvoiceRequestContents {
-                       inner: self.invoice_request,
+                       #[cfg(not(c_bindings))]
+                       inner: $self.invoice_request,
+                       #[cfg(c_bindings)]
+                       inner: $self.invoice_request.clone(),
                        payer_id,
                };
-               let unsigned_invoice_request = UnsignedInvoiceRequest::new(self.offer, invoice_request);
+               let unsigned_invoice_request = UnsignedInvoiceRequest::new($self.offer, invoice_request);
 
                (unsigned_invoice_request, keys, secp_ctx)
        }
-}
+} }
 
-impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, ExplicitPayerId, T> {
-       /// Builds an unsigned [`InvoiceRequest`] after checking for valid semantics. It can be signed
-       /// by [`UnsignedInvoiceRequest::sign`].
-       pub fn build(self) -> Result<UnsignedInvoiceRequest, Bolt12SemanticError> {
-               let (unsigned_invoice_request, keys, _) = self.build_with_checks()?;
-               debug_assert!(keys.is_none());
-               Ok(unsigned_invoice_request)
+#[cfg(test)]
+macro_rules! invoice_request_builder_test_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
+) => {
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       fn chain_unchecked($($self_mut)* $self: $self_type, network: Network) -> $return_type {
+               let chain = ChainHash::using_genesis_block(network);
+               $self.invoice_request.chain = Some(chain);
+               $return_value
        }
-}
 
-impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T> {
-       /// Builds a signed [`InvoiceRequest`] after checking for valid semantics.
-       pub fn build_and_sign(self) -> Result<InvoiceRequest, Bolt12SemanticError> {
-               let (unsigned_invoice_request, keys, secp_ctx) = self.build_with_checks()?;
-               debug_assert!(keys.is_some());
-
-               let secp_ctx = secp_ctx.unwrap();
-               let keys = keys.unwrap();
-               let invoice_request = unsigned_invoice_request
-                       .sign::<_, Infallible>(
-                               |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
-                       )
-                       .unwrap();
-               Ok(invoice_request)
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       fn amount_msats_unchecked($($self_mut)* $self: $self_type, amount_msats: u64) -> $return_type {
+               $self.invoice_request.amount_msats = Some(amount_msats);
+               $return_value
        }
-}
 
-#[cfg(test)]
-impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, P, T> {
-       fn chain_unchecked(mut self, network: Network) -> Self {
-               let chain = ChainHash::using_genesis_block(network);
-               self.invoice_request.chain = Some(chain);
-               self
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       fn features_unchecked($($self_mut)* $self: $self_type, features: InvoiceRequestFeatures) -> $return_type {
+               $self.invoice_request.features = features;
+               $return_value
        }
 
-       fn amount_msats_unchecked(mut self, amount_msats: u64) -> Self {
-               self.invoice_request.amount_msats = Some(amount_msats);
-               self
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       fn quantity_unchecked($($self_mut)* $self: $self_type, quantity: u64) -> $return_type {
+               $self.invoice_request.quantity = Some(quantity);
+               $return_value
        }
 
-       fn features_unchecked(mut self, features: InvoiceRequestFeatures) -> Self {
-               self.invoice_request.features = features;
-               self
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       pub(super) fn build_unchecked($self: $self_type) -> UnsignedInvoiceRequest {
+               $self.build_without_checks().0
        }
+} }
+
+impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, ExplicitPayerId, T> {
+       invoice_request_explicit_payer_id_builder_methods!(self, Self);
+}
+
+impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T> {
+       invoice_request_derived_payer_id_builder_methods!(self, Self, T);
+}
+
+impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, P, T> {
+       invoice_request_builder_methods!(self, Self, Self, self, T, mut);
+
+       #[cfg(test)]
+       invoice_request_builder_test_methods!(self, Self, Self, self, mut);
+}
+
+#[cfg(all(c_bindings, not(test)))]
+impl<'a, 'b> InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b> {
+       invoice_request_explicit_payer_id_builder_methods!(self, &mut Self);
+       invoice_request_builder_methods!(self, &mut Self, (), (), secp256k1::All);
+}
+
+#[cfg(all(c_bindings, test))]
+impl<'a, 'b> InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b> {
+       invoice_request_explicit_payer_id_builder_methods!(self, &mut Self);
+       invoice_request_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All);
+       invoice_request_builder_test_methods!(self, &mut Self, &mut Self, self);
+}
+
+#[cfg(all(c_bindings, not(test)))]
+impl<'a, 'b> InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b> {
+       invoice_request_derived_payer_id_builder_methods!(self, &mut Self, secp256k1::All);
+       invoice_request_builder_methods!(self, &mut Self, (), (), secp256k1::All);
+}
+
+#[cfg(all(c_bindings, test))]
+impl<'a, 'b> InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b> {
+       invoice_request_derived_payer_id_builder_methods!(self, &mut Self, secp256k1::All);
+       invoice_request_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All);
+       invoice_request_builder_test_methods!(self, &mut Self, &mut Self, self);
+}
 
-       fn quantity_unchecked(mut self, quantity: u64) -> Self {
-               self.invoice_request.quantity = Some(quantity);
-               self
+#[cfg(c_bindings)]
+impl<'a, 'b> From<InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b>>
+for InvoiceRequestBuilder<'a, 'b, ExplicitPayerId, secp256k1::All> {
+       fn from(builder: InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b>) -> Self {
+               let InvoiceRequestWithExplicitPayerIdBuilder {
+                       offer, invoice_request, payer_id, payer_id_strategy, secp_ctx,
+               } = builder;
+
+               Self {
+                       offer, invoice_request, payer_id, payer_id_strategy, secp_ctx,
+               }
        }
+}
+
+#[cfg(c_bindings)]
+impl<'a, 'b> From<InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b>>
+for InvoiceRequestBuilder<'a, 'b, DerivedPayerId, secp256k1::All> {
+       fn from(builder: InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b>) -> Self {
+               let InvoiceRequestWithDerivedPayerIdBuilder {
+                       offer, invoice_request, payer_id, payer_id_strategy, secp_ctx,
+               } = builder;
 
-       pub(super) fn build_unchecked(self) -> UnsignedInvoiceRequest {
-               self.build_without_checks().0
+               Self {
+                       offer, invoice_request, payer_id, payer_id_strategy, secp_ctx,
+               }
        }
 }
 
@@ -368,6 +493,30 @@ pub struct UnsignedInvoiceRequest {
        tagged_hash: TaggedHash,
 }
 
+/// A function for signing an [`UnsignedInvoiceRequest`].
+pub trait SignInvoiceRequestFn {
+       /// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream.
+       fn sign_invoice_request(&self, message: &UnsignedInvoiceRequest) -> Result<Signature, ()>;
+}
+
+impl<F> SignInvoiceRequestFn for F
+where
+       F: Fn(&UnsignedInvoiceRequest) -> Result<Signature, ()>,
+{
+       fn sign_invoice_request(&self, message: &UnsignedInvoiceRequest) -> Result<Signature, ()> {
+               self(message)
+       }
+}
+
+impl<F> SignFn<UnsignedInvoiceRequest> for F
+where
+       F: SignInvoiceRequestFn,
+{
+       fn sign(&self, message: &UnsignedInvoiceRequest) -> Result<Signature, ()> {
+               self.sign_invoice_request(message)
+       }
+}
+
 impl UnsignedInvoiceRequest {
        fn new(offer: &Offer, contents: InvoiceRequestContents) -> Self {
                // Use the offer bytes instead of the offer TLV stream as the offer may have contained
@@ -389,31 +538,48 @@ impl UnsignedInvoiceRequest {
        pub fn tagged_hash(&self) -> &TaggedHash {
                &self.tagged_hash
        }
+}
 
+macro_rules! unsigned_invoice_request_sign_method { (
+       $self: ident, $self_type: ty $(, $self_mut: tt)?
+) => {
        /// Signs the [`TaggedHash`] of the invoice request using the given function.
        ///
        /// Note: The hash computation may have included unknown, odd TLV records.
-       ///
-       /// This is not exported to bindings users as functions are not yet mapped.
-       pub fn sign<F, E>(mut self, sign: F) -> Result<InvoiceRequest, SignError<E>>
-       where
-               F: FnOnce(&Self) -> Result<Signature, E>
-       {
-               let pubkey = self.contents.payer_id;
-               let signature = merkle::sign_message(sign, &self, pubkey)?;
+       pub fn sign<F: SignInvoiceRequestFn>(
+               $($self_mut)* $self: $self_type, sign: F
+       ) -> Result<InvoiceRequest, SignError> {
+               let pubkey = $self.contents.payer_id;
+               let signature = merkle::sign_message(sign, &$self, pubkey)?;
 
                // Append the signature TLV record to the bytes.
                let signature_tlv_stream = SignatureTlvStreamRef {
                        signature: Some(&signature),
                };
-               signature_tlv_stream.write(&mut self.bytes).unwrap();
+               signature_tlv_stream.write(&mut $self.bytes).unwrap();
 
                Ok(InvoiceRequest {
-                       bytes: self.bytes,
-                       contents: self.contents,
+                       #[cfg(not(c_bindings))]
+                       bytes: $self.bytes,
+                       #[cfg(c_bindings)]
+                       bytes: $self.bytes.clone(),
+                       #[cfg(not(c_bindings))]
+                       contents: $self.contents,
+                       #[cfg(c_bindings)]
+                       contents: $self.contents.clone(),
                        signature,
                })
        }
+} }
+
+#[cfg(not(c_bindings))]
+impl UnsignedInvoiceRequest {
+       unsigned_invoice_request_sign_method!(self, Self, mut);
+}
+
+#[cfg(c_bindings)]
+impl UnsignedInvoiceRequest {
+       unsigned_invoice_request_sign_method!(self, &mut Self);
 }
 
 impl AsRef<TaggedHash> for UnsignedInvoiceRequest {
@@ -526,35 +692,25 @@ impl UnsignedInvoiceRequest {
        invoice_request_accessors!(self, self.contents);
 }
 
-impl InvoiceRequest {
-       offer_accessors!(self, self.contents.inner.offer);
-       invoice_request_accessors!(self, self.contents);
-
-       /// Signature of the invoice request using [`payer_id`].
-       ///
-       /// [`payer_id`]: Self::payer_id
-       pub fn signature(&self) -> Signature {
-               self.signature
-       }
-
+macro_rules! invoice_request_respond_with_explicit_signing_pubkey_methods { (
+       $self: ident, $contents: expr, $builder: ty
+) => {
        /// Creates an [`InvoiceBuilder`] for the request with the given required fields and using the
        /// [`Duration`] since [`std::time::SystemTime::UNIX_EPOCH`] as the creation time.
        ///
        /// See [`InvoiceRequest::respond_with_no_std`] for further details where the aforementioned
        /// creation time is used for the `created_at` parameter.
        ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
        /// [`Duration`]: core::time::Duration
        #[cfg(feature = "std")]
        pub fn respond_with(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash
-       ) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash
+       ) -> Result<$builder, Bolt12SemanticError> {
                let created_at = std::time::SystemTime::now()
                        .duration_since(std::time::SystemTime::UNIX_EPOCH)
                        .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
 
-               self.respond_with_no_std(payment_paths, payment_hash, created_at)
+               $contents.respond_with_no_std(payment_paths, payment_hash, created_at)
        }
 
        /// Creates an [`InvoiceBuilder`] for the request with the given required fields.
@@ -578,36 +734,72 @@ impl InvoiceRequest {
        /// If the originating [`Offer`] was created using [`OfferBuilder::deriving_signing_pubkey`],
        /// then use [`InvoiceRequest::verify`] and [`VerifiedInvoiceRequest`] methods instead.
        ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
        /// [`Bolt12Invoice::created_at`]: crate::offers::invoice::Bolt12Invoice::created_at
        /// [`OfferBuilder::deriving_signing_pubkey`]: crate::offers::offer::OfferBuilder::deriving_signing_pubkey
        pub fn respond_with_no_std(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
                created_at: core::time::Duration
-       ) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
-               if self.invoice_request_features().requires_unknown_bits() {
+       ) -> Result<$builder, Bolt12SemanticError> {
+               if $contents.invoice_request_features().requires_unknown_bits() {
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
 
-               InvoiceBuilder::for_offer(self, payment_paths, created_at, payment_hash)
+               <$builder>::for_offer(&$contents, payment_paths, created_at, payment_hash)
        }
+} }
 
+macro_rules! invoice_request_verify_method { ($self: ident, $self_type: ty) => {
        /// Verifies that the request was for an offer created using the given key. Returns the verified
        /// request which contains the derived keys needed to sign a [`Bolt12Invoice`] for the request
        /// if they could be extracted from the metadata.
        ///
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
-       pub fn verify<T: secp256k1::Signing>(
-               self, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
+       pub fn verify<
+               #[cfg(not(c_bindings))]
+               T: secp256k1::Signing
+       >(
+               $self: $self_type, key: &ExpandedKey,
+               #[cfg(not(c_bindings))]
+               secp_ctx: &Secp256k1<T>,
+               #[cfg(c_bindings)]
+               secp_ctx: &Secp256k1<secp256k1::All>,
        ) -> Result<VerifiedInvoiceRequest, ()> {
-               let keys = self.contents.inner.offer.verify(&self.bytes, key, secp_ctx)?;
+               let keys = $self.contents.inner.offer.verify(&$self.bytes, key, secp_ctx)?;
                Ok(VerifiedInvoiceRequest {
-                       inner: self,
+                       #[cfg(not(c_bindings))]
+                       inner: $self,
+                       #[cfg(c_bindings)]
+                       inner: $self.clone(),
                        keys,
                })
        }
 
+} }
+
+#[cfg(not(c_bindings))]
+impl InvoiceRequest {
+       offer_accessors!(self, self.contents.inner.offer);
+       invoice_request_accessors!(self, self.contents);
+       invoice_request_respond_with_explicit_signing_pubkey_methods!(self, self, InvoiceBuilder<ExplicitSigningPubkey>);
+       invoice_request_verify_method!(self, Self);
+}
+
+#[cfg(c_bindings)]
+impl InvoiceRequest {
+       offer_accessors!(self, self.contents.inner.offer);
+       invoice_request_accessors!(self, self.contents);
+       invoice_request_respond_with_explicit_signing_pubkey_methods!(self, self, InvoiceWithExplicitSigningPubkeyBuilder);
+       invoice_request_verify_method!(self, &Self);
+}
+
+impl InvoiceRequest {
+       /// Signature of the invoice request using [`payer_id`].
+       ///
+       /// [`payer_id`]: Self::payer_id
+       pub fn signature(&self) -> Signature {
+               self.signature
+       }
+
        pub(crate) fn as_tlv_stream(&self) -> FullInvoiceRequestTlvStreamRef {
                let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream) =
                        self.contents.as_tlv_stream();
@@ -618,55 +810,25 @@ impl InvoiceRequest {
        }
 }
 
-impl VerifiedInvoiceRequest {
-       offer_accessors!(self, self.inner.contents.inner.offer);
-       invoice_request_accessors!(self, self.inner.contents);
-
-       /// Creates an [`InvoiceBuilder`] for the request with the given required fields and using the
-       /// [`Duration`] since [`std::time::SystemTime::UNIX_EPOCH`] as the creation time.
-       ///
-       /// See [`InvoiceRequest::respond_with_no_std`] for further details.
-       ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
-       /// [`Duration`]: core::time::Duration
-       #[cfg(feature = "std")]
-       pub fn respond_with(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash
-       ) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
-               self.inner.respond_with(payment_paths, payment_hash)
-       }
-
-       /// Creates an [`InvoiceBuilder`] for the request with the given required fields.
-       ///
-       /// See [`InvoiceRequest::respond_with_no_std`] for further details.
-       ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       pub fn respond_with_no_std(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
-               created_at: core::time::Duration
-       ) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
-               self.inner.respond_with_no_std(payment_paths, payment_hash, created_at)
-       }
-
+macro_rules! invoice_request_respond_with_derived_signing_pubkey_methods { (
+       $self: ident, $contents: expr, $builder: ty
+) => {
        /// Creates an [`InvoiceBuilder`] for the request using the given required fields and that uses
        /// derived signing keys from the originating [`Offer`] to sign the [`Bolt12Invoice`]. Must use
        /// the same [`ExpandedKey`] as the one used to create the offer.
        ///
        /// See [`InvoiceRequest::respond_with`] for further details.
        ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        #[cfg(feature = "std")]
        pub fn respond_using_derived_keys(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash
-       ) -> Result<InvoiceBuilder<DerivedSigningPubkey>, Bolt12SemanticError> {
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash
+       ) -> Result<$builder, Bolt12SemanticError> {
                let created_at = std::time::SystemTime::now()
                        .duration_since(std::time::SystemTime::UNIX_EPOCH)
                        .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
 
-               self.respond_using_derived_keys_no_std(payment_paths, payment_hash, created_at)
+               $self.respond_using_derived_keys_no_std(payment_paths, payment_hash, created_at)
        }
 
        /// Creates an [`InvoiceBuilder`] for the request using the given required fields and that uses
@@ -675,26 +837,37 @@ impl VerifiedInvoiceRequest {
        ///
        /// See [`InvoiceRequest::respond_with_no_std`] for further details.
        ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        pub fn respond_using_derived_keys_no_std(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
                created_at: core::time::Duration
-       ) -> Result<InvoiceBuilder<DerivedSigningPubkey>, Bolt12SemanticError> {
-               if self.inner.invoice_request_features().requires_unknown_bits() {
+       ) -> Result<$builder, Bolt12SemanticError> {
+               if $self.inner.invoice_request_features().requires_unknown_bits() {
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
 
-               let keys = match self.keys {
+               let keys = match $self.keys {
                        None => return Err(Bolt12SemanticError::InvalidMetadata),
                        Some(keys) => keys,
                };
 
-               InvoiceBuilder::for_offer_using_keys(
-                       &self.inner, payment_paths, created_at, payment_hash, keys
+               <$builder>::for_offer_using_keys(
+                       &$self.inner, payment_paths, created_at, payment_hash, keys
                )
        }
+} }
+
+impl VerifiedInvoiceRequest {
+       offer_accessors!(self, self.inner.contents.inner.offer);
+       invoice_request_accessors!(self, self.inner.contents);
+       #[cfg(not(c_bindings))]
+       invoice_request_respond_with_explicit_signing_pubkey_methods!(self, self.inner, InvoiceBuilder<ExplicitSigningPubkey>);
+       #[cfg(c_bindings)]
+       invoice_request_respond_with_explicit_signing_pubkey_methods!(self, self.inner, InvoiceWithExplicitSigningPubkeyBuilder);
+       #[cfg(not(c_bindings))]
+       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);
 }
 
 impl InvoiceRequestContents {
@@ -930,7 +1103,7 @@ mod tests {
        use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::network::constants::Network;
        use bitcoin::secp256k1::{KeyPair, Secp256k1, SecretKey, self};
-       use core::convert::{Infallible, TryFrom};
+       use core::convert::TryFrom;
        use core::num::NonZeroU64;
        #[cfg(feature = "std")]
        use core::time::Duration;
@@ -941,7 +1114,15 @@ mod tests {
        use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
        use crate::offers::invoice::{Bolt12Invoice, SIGNATURE_TAG as INVOICE_SIGNATURE_TAG};
        use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
-       use crate::offers::offer::{Amount, OfferBuilder, OfferTlvStreamRef, Quantity};
+       use crate::offers::offer::{Amount, OfferTlvStreamRef, Quantity};
+       #[cfg(not(c_bindings))]
+       use {
+               crate::offers::offer::OfferBuilder,
+       };
+       #[cfg(c_bindings)]
+       use {
+               crate::offers::offer::OfferWithExplicitMetadataBuilder as OfferBuilder,
+       };
        use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
        use crate::offers::payer::PayerTlvStreamRef;
        use crate::offers::test_utils::*;
@@ -955,6 +1136,8 @@ mod tests {
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
                        .build().unwrap();
+               #[cfg(c_bindings)]
+               let mut unsigned_invoice_request = unsigned_invoice_request;
 
                let mut buffer = Vec::new();
                unsigned_invoice_request.write(&mut buffer).unwrap();
@@ -1550,10 +1733,10 @@ mod tests {
                        .build().unwrap()
                        .request_invoice(vec![1; 32], payer_pubkey()).unwrap()
                        .build().unwrap()
-                       .sign(|_| Err(()))
+                       .sign(fail_sign)
                {
                        Ok(_) => panic!("expected error"),
-                       Err(e) => assert_eq!(e, SignError::Signing(())),
+                       Err(e) => assert_eq!(e, SignError::Signing),
                }
 
                match OfferBuilder::new("foo".into(), recipient_pubkey())
@@ -1964,8 +2147,8 @@ mod tests {
                        .build().unwrap()
                        .request_invoice(vec![1; 32], keys.public_key()).unwrap()
                        .build().unwrap()
-                       .sign::<_, Infallible>(
-                               |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
+                       .sign(|message: &UnsignedInvoiceRequest|
+                               Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
                        )
                        .unwrap();
 
index ced3b08b7c2f3a33becd7b2e4f2a444f909a66c9..941bf196716d8b5ee2d2e51d2c4db95716bdf869 100644 (file)
@@ -76,13 +76,28 @@ impl AsRef<TaggedHash> for TaggedHash {
 
 /// Error when signing messages.
 #[derive(Debug, PartialEq)]
-pub enum SignError<E> {
+pub enum SignError {
        /// User-defined error when signing the message.
-       Signing(E),
+       Signing,
        /// Error when verifying the produced signature using the given pubkey.
        Verification(secp256k1::Error),
 }
 
+/// A function for signing a [`TaggedHash`].
+pub(super) trait SignFn<T: AsRef<TaggedHash>> {
+       /// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream.
+       fn sign(&self, message: &T) -> Result<Signature, ()>;
+}
+
+impl<F> SignFn<TaggedHash> for F
+where
+       F: Fn(&TaggedHash) -> Result<Signature, ()>,
+{
+       fn sign(&self, message: &TaggedHash) -> Result<Signature, ()> {
+               self(message)
+       }
+}
+
 /// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream, checking if it
 /// can be verified with the supplied `pubkey`.
 ///
@@ -92,14 +107,14 @@ pub enum SignError<E> {
 ///
 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
-pub(super) fn sign_message<F, E, T>(
-       sign: F, message: &T, pubkey: PublicKey,
-) -> Result<Signature, SignError<E>>
+pub(super) fn sign_message<F, T>(
+       f: F, message: &T, pubkey: PublicKey,
+) -> Result<Signature, SignError>
 where
-       F: FnOnce(&T) -> Result<Signature, E>,
+       F: SignFn<T>,
        T: AsRef<TaggedHash>,
 {
-       let signature = sign(message).map_err(|e| SignError::Signing(e))?;
+       let signature = f.sign(message).map_err(|()| SignError::Signing)?;
 
        let digest = message.as_ref().as_digest();
        let pubkey = pubkey.into();
@@ -276,9 +291,8 @@ mod tests {
        use bitcoin::hashes::hex::FromHex;
        use bitcoin::secp256k1::{KeyPair, Message, Secp256k1, SecretKey};
        use bitcoin::secp256k1::schnorr::Signature;
-       use core::convert::Infallible;
        use crate::offers::offer::{Amount, OfferBuilder};
-       use crate::offers::invoice_request::InvoiceRequest;
+       use crate::offers::invoice_request::{InvoiceRequest, UnsignedInvoiceRequest};
        use crate::offers::parse::Bech32Encode;
        use crate::offers::test_utils::{payer_pubkey, recipient_pubkey};
        use crate::util::ser::Writeable;
@@ -321,8 +335,8 @@ mod tests {
                        .build_unchecked()
                        .request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
                        .build_unchecked()
-                       .sign::<_, Infallible>(
-                               |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
+                       .sign(|message: &UnsignedInvoiceRequest|
+                               Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
                        )
                        .unwrap();
                assert_eq!(
@@ -375,8 +389,8 @@ mod tests {
                        .build_unchecked()
                        .request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
                        .build_unchecked()
-                       .sign::<_, Infallible>(
-                               |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
+                       .sign(|message: &UnsignedInvoiceRequest|
+                               Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
                        )
                        .unwrap();
 
@@ -407,8 +421,8 @@ mod tests {
                        .build_unchecked()
                        .request_invoice(vec![0; 8], payer_keys.public_key()).unwrap()
                        .build_unchecked()
-                       .sign::<_, Infallible>(
-                               |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
+                       .sign(|message: &UnsignedInvoiceRequest|
+                               Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &payer_keys))
                        )
                        .unwrap();
 
index 802813fd180f70d9f6006cdc126a69f5c5f8f6e3..bf5e50deb77b8c290c1385f3459b76fb17bfbd6d 100644 (file)
@@ -80,6 +80,7 @@ use bitcoin::blockdata::constants::ChainHash;
 use bitcoin::network::constants::Network;
 use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, self};
 use core::convert::TryFrom;
+use core::hash::{Hash, Hasher};
 use core::num::NonZeroU64;
 use core::ops::Deref;
 use core::str::FromStr;
@@ -91,13 +92,21 @@ 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::invoice_request::{DerivedPayerId, ExplicitPayerId, InvoiceRequestBuilder};
 use crate::offers::merkle::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::string::PrintableString;
 
+#[cfg(not(c_bindings))]
+use {
+       crate::offers::invoice_request::{DerivedPayerId, ExplicitPayerId, InvoiceRequestBuilder},
+};
+#[cfg(c_bindings)]
+use {
+       crate::offers::invoice_request::{InvoiceRequestWithDerivedPayerIdBuilder, InvoiceRequestWithExplicitPayerIdBuilder},
+};
+
 use crate::prelude::*;
 
 #[cfg(feature = "std")]
@@ -118,6 +127,34 @@ pub struct OfferBuilder<'a, M: MetadataStrategy, T: secp256k1::Signing> {
        secp_ctx: Option<&'a Secp256k1<T>>,
 }
 
+/// Builds an [`Offer`] for the "offer to be paid" flow.
+///
+/// See [module-level documentation] for usage.
+///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+///
+/// [module-level documentation]: self
+#[cfg(c_bindings)]
+pub struct OfferWithExplicitMetadataBuilder<'a> {
+       offer: OfferContents,
+       metadata_strategy: core::marker::PhantomData<ExplicitMetadata>,
+       secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
+}
+
+/// Builds an [`Offer`] for the "offer to be paid" flow.
+///
+/// See [module-level documentation] for usage.
+///
+/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+///
+/// [module-level documentation]: self
+#[cfg(c_bindings)]
+pub struct OfferWithDerivedMetadataBuilder<'a> {
+       offer: OfferContents,
+       metadata_strategy: core::marker::PhantomData<DerivedMetadata>,
+       secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
+}
+
 /// Indicates how [`Offer::metadata`] may be set.
 ///
 /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
@@ -134,9 +171,12 @@ pub struct ExplicitMetadata {}
 pub struct DerivedMetadata {}
 
 impl MetadataStrategy for ExplicitMetadata {}
+
 impl MetadataStrategy for DerivedMetadata {}
 
-impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
+macro_rules! offer_explicit_metadata_builder_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr
+) => {
        /// Creates a new builder for an offer setting the [`Offer::description`] and using the
        /// [`Offer::signing_pubkey`] for signing invoices. The associated secret key must be remembered
        /// while the offer is valid.
@@ -151,7 +191,7 @@ impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
        /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
        /// [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder
        pub fn new(description: String, signing_pubkey: PublicKey) -> Self {
-               OfferBuilder {
+               Self {
                        offer: OfferContents {
                                chains: None, metadata: None, amount: None, description,
                                features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
@@ -165,13 +205,13 @@ impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
        /// Sets the [`Offer::metadata`] to the given bytes.
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn metadata(mut self, metadata: Vec<u8>) -> Result<Self, Bolt12SemanticError> {
-               self.offer.metadata = Some(Metadata::Bytes(metadata));
-               Ok(self)
+       pub fn metadata(mut $self: $self_type, metadata: Vec<u8>) -> Result<$return_type, Bolt12SemanticError> {
+               $self.offer.metadata = Some(Metadata::Bytes(metadata));
+               Ok($return_value)
        }
-}
+} }
 
-impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
+macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => {
        /// Similar to [`OfferBuilder::new`] except, if [`OfferBuilder::path`] is called, the signing
        /// pubkey is derived from the given [`ExpandedKey`] and [`EntropySource`]. This provides
        /// recipient privacy by using a different signing pubkey for each offer. Otherwise, the
@@ -185,12 +225,12 @@ impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
        /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
        pub fn deriving_signing_pubkey<ES: Deref>(
                description: String, node_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
-               secp_ctx: &'a Secp256k1<T>
+               secp_ctx: &'a Secp256k1<$secp_context>
        ) -> Self where ES::Target: EntropySource {
                let nonce = Nonce::from_entropy_source(entropy_source);
                let derivation_material = MetadataMaterial::new(nonce, expanded_key, IV_BYTES, None);
                let metadata = Metadata::DerivedSigningPubkey(derivation_material);
-               OfferBuilder {
+               Self {
                        offer: OfferContents {
                                chains: None, metadata: Some(metadata), amount: None, description,
                                features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
@@ -200,17 +240,19 @@ impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
                        secp_ctx: Some(secp_ctx),
                }
        }
-}
+} }
 
-impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
+macro_rules! offer_builder_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
+) => {
        /// Adds the chain hash of the given [`Network`] to [`Offer::chains`]. If not called,
        /// the chain hash of [`Network::Bitcoin`] is assumed to be the only one supported.
        ///
        /// See [`Offer::chains`] on how this relates to the payment currency.
        ///
        /// Successive calls to this method will add another chain hash.
-       pub fn chain(self, network: Network) -> Self {
-               self.chain_hash(ChainHash::using_genesis_block(network))
+       pub fn chain($self: $self_type, network: Network) -> $return_type {
+               $self.chain_hash(ChainHash::using_genesis_block(network))
        }
 
        /// Adds the [`ChainHash`] to [`Offer::chains`]. If not called, the chain hash of
@@ -219,45 +261,45 @@ impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
        /// See [`Offer::chains`] on how this relates to the payment currency.
        ///
        /// Successive calls to this method will add another chain hash.
-       pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Self {
-               let chains = self.offer.chains.get_or_insert_with(Vec::new);
+       pub(crate) fn chain_hash($($self_mut)* $self: $self_type, chain: ChainHash) -> $return_type {
+               let chains = $self.offer.chains.get_or_insert_with(Vec::new);
                if !chains.contains(&chain) {
                        chains.push(chain);
                }
 
-               self
+               $return_value
        }
 
        /// Sets the [`Offer::amount`] as an [`Amount::Bitcoin`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn amount_msats(self, amount_msats: u64) -> Self {
-               self.amount(Amount::Bitcoin { amount_msats })
+       pub fn amount_msats($self: $self_type, amount_msats: u64) -> $return_type {
+               $self.amount(Amount::Bitcoin { amount_msats })
        }
 
        /// Sets the [`Offer::amount`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub(super) fn amount(mut self, amount: Amount) -> Self {
-               self.offer.amount = Some(amount);
-               self
+       pub(super) fn amount($($self_mut)* $self: $self_type, amount: Amount) -> $return_type {
+               $self.offer.amount = Some(amount);
+               $return_value
        }
 
        /// Sets the [`Offer::absolute_expiry`] as seconds since the Unix epoch. Any expiry that has
        /// already passed is valid and can be checked for using [`Offer::is_expired`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn absolute_expiry(mut self, absolute_expiry: Duration) -> Self {
-               self.offer.absolute_expiry = Some(absolute_expiry);
-               self
+       pub fn absolute_expiry($($self_mut)* $self: $self_type, absolute_expiry: Duration) -> $return_type {
+               $self.offer.absolute_expiry = Some(absolute_expiry);
+               $return_value
        }
 
        /// Sets the [`Offer::issuer`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn issuer(mut self, issuer: String) -> Self {
-               self.offer.issuer = Some(issuer);
-               self
+       pub fn issuer($($self_mut)* $self: $self_type, issuer: String) -> $return_type {
+               $self.offer.issuer = Some(issuer);
+               $return_value
        }
 
        /// Adds a blinded path to [`Offer::paths`]. Must include at least one path if only connected by
@@ -265,23 +307,23 @@ impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
        ///
        /// Successive calls to this method will add another blinded path. Caller is responsible for not
        /// adding duplicate paths.
-       pub fn path(mut self, path: BlindedPath) -> Self {
-               self.offer.paths.get_or_insert_with(Vec::new).push(path);
-               self
+       pub fn path($($self_mut)* $self: $self_type, path: BlindedPath) -> $return_type {
+               $self.offer.paths.get_or_insert_with(Vec::new).push(path);
+               $return_value
        }
 
        /// Sets the quantity of items for [`Offer::supported_quantity`]. If not called, defaults to
        /// [`Quantity::One`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn supported_quantity(mut self, quantity: Quantity) -> Self {
-               self.offer.supported_quantity = quantity;
-               self
+       pub fn supported_quantity($($self_mut)* $self: $self_type, quantity: Quantity) -> $return_type {
+               $self.offer.supported_quantity = quantity;
+               $return_value
        }
 
        /// Builds an [`Offer`] from the builder's settings.
-       pub fn build(mut self) -> Result<Offer, Bolt12SemanticError> {
-               match self.offer.amount {
+       pub fn build($($self_mut)* $self: $self_type) -> Result<Offer, Bolt12SemanticError> {
+               match $self.offer.amount {
                        Some(Amount::Bitcoin { amount_msats }) => {
                                if amount_msats > MAX_VALUE_MSAT {
                                        return Err(Bolt12SemanticError::InvalidAmount);
@@ -291,61 +333,129 @@ impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
                        None => {},
                }
 
-               if let Some(chains) = &self.offer.chains {
-                       if chains.len() == 1 && chains[0] == self.offer.implied_chain() {
-                               self.offer.chains = None;
+               if let Some(chains) = &$self.offer.chains {
+                       if chains.len() == 1 && chains[0] == $self.offer.implied_chain() {
+                               $self.offer.chains = None;
                        }
                }
 
-               Ok(self.build_without_checks())
+               Ok($self.build_without_checks())
        }
 
-       fn build_without_checks(mut self) -> Offer {
+       fn build_without_checks($($self_mut)* $self: $self_type) -> Offer {
                // Create the metadata for stateless verification of an InvoiceRequest.
-               if let Some(mut metadata) = self.offer.metadata.take() {
+               if let Some(mut metadata) = $self.offer.metadata.take() {
                        if metadata.has_derivation_material() {
-                               if self.offer.paths.is_none() {
+                               if $self.offer.paths.is_none() {
                                        metadata = metadata.without_keys();
                                }
 
-                               let mut tlv_stream = self.offer.as_tlv_stream();
+                               let mut tlv_stream = $self.offer.as_tlv_stream();
                                debug_assert_eq!(tlv_stream.metadata, None);
                                tlv_stream.metadata = None;
                                if metadata.derives_recipient_keys() {
                                        tlv_stream.node_id = None;
                                }
 
-                               let (derived_metadata, keys) = metadata.derive_from(tlv_stream, self.secp_ctx);
+                               let (derived_metadata, keys) = metadata.derive_from(tlv_stream, $self.secp_ctx);
                                metadata = derived_metadata;
                                if let Some(keys) = keys {
-                                       self.offer.signing_pubkey = keys.public_key();
+                                       $self.offer.signing_pubkey = keys.public_key();
                                }
                        }
 
-                       self.offer.metadata = Some(metadata);
+                       $self.offer.metadata = Some(metadata);
                }
 
                let mut bytes = Vec::new();
-               self.offer.write(&mut bytes).unwrap();
-
-               Offer { bytes, contents: self.offer }
+               $self.offer.write(&mut bytes).unwrap();
+
+               Offer {
+                       bytes,
+                       #[cfg(not(c_bindings))]
+                       contents: $self.offer,
+                       #[cfg(c_bindings)]
+                       contents: $self.offer.clone()
+               }
        }
-}
+} }
 
 #[cfg(test)]
-impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
-       fn features_unchecked(mut self, features: OfferFeatures) -> Self {
-               self.offer.features = features;
-               self
+macro_rules! offer_builder_test_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
+) => {
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       fn features_unchecked($($self_mut)* $self: $self_type, features: OfferFeatures) -> $return_type {
+               $self.offer.features = features;
+               $return_value
        }
 
-       pub(crate) fn clear_paths(mut self) -> Self {
-               self.offer.paths = None;
-               self
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       pub(crate) fn clear_chains($($self_mut)* $self: $self_type) -> $return_type {
+               $self.offer.chains = None;
+               $return_value
        }
 
-       pub(super) fn build_unchecked(self) -> Offer {
-               self.build_without_checks()
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       pub(crate) fn clear_paths($($self_mut)* $self: $self_type) -> $return_type {
+               $self.offer.paths = None;
+               $return_value
+       }
+
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       pub(super) fn build_unchecked($self: $self_type) -> Offer {
+               $self.build_without_checks()
+       }
+} }
+
+impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
+       offer_builder_methods!(self, Self, Self, self, mut);
+
+       #[cfg(test)]
+       offer_builder_test_methods!(self, Self, Self, self, mut);
+}
+
+impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
+       offer_explicit_metadata_builder_methods!(self, Self, Self, self);
+}
+
+impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
+       offer_derived_metadata_builder_methods!(T);
+}
+
+#[cfg(all(c_bindings, not(test)))]
+impl<'a> OfferWithExplicitMetadataBuilder<'a> {
+       offer_explicit_metadata_builder_methods!(self, &mut Self, (), ());
+       offer_builder_methods!(self, &mut Self, (), ());
+}
+
+#[cfg(all(c_bindings, test))]
+impl<'a> OfferWithExplicitMetadataBuilder<'a> {
+       offer_explicit_metadata_builder_methods!(self, &mut Self, &mut Self, self);
+       offer_builder_methods!(self, &mut Self, &mut Self, self);
+       offer_builder_test_methods!(self, &mut Self, &mut Self, self);
+}
+
+#[cfg(all(c_bindings, not(test)))]
+impl<'a> OfferWithDerivedMetadataBuilder<'a> {
+       offer_derived_metadata_builder_methods!(secp256k1::All);
+       offer_builder_methods!(self, &mut Self, (), ());
+}
+
+#[cfg(all(c_bindings, test))]
+impl<'a> OfferWithDerivedMetadataBuilder<'a> {
+       offer_derived_metadata_builder_methods!(secp256k1::All);
+       offer_builder_methods!(self, &mut Self, &mut Self, self);
+       offer_builder_test_methods!(self, &mut Self, &mut Self, self);
+}
+
+#[cfg(c_bindings)]
+impl<'a> From<OfferBuilder<'a, DerivedMetadata, secp256k1::All>>
+for OfferWithDerivedMetadataBuilder<'a> {
+       fn from(builder: OfferBuilder<'a, DerivedMetadata, secp256k1::All>) -> Self {
+               let OfferBuilder { offer, metadata_strategy, secp_ctx } = builder;
+
+               Self { offer, metadata_strategy, secp_ctx }
        }
 }
 
@@ -363,7 +473,6 @@ impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
 #[derive(Clone, Debug)]
-#[cfg_attr(test, derive(PartialEq))]
 pub struct Offer {
        // The serialized offer. Needed when creating an `InvoiceRequest` if the offer contains unknown
        // fields.
@@ -489,7 +598,9 @@ impl Offer {
        pub fn expects_quantity(&self) -> bool {
                self.contents.expects_quantity()
        }
+}
 
+macro_rules! request_invoice_derived_payer_id { ($self: ident, $builder: ty) => {
        /// Similar to [`Offer::request_invoice`] except it:
        /// - derives the [`InvoiceRequest::payer_id`] such that a different key can be used for each
        ///   request,
@@ -501,50 +612,52 @@ impl Offer {
        ///
        /// Useful to protect the sender's privacy.
        ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
        /// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id
        /// [`InvoiceRequest::payer_metadata`]: crate::offers::invoice_request::InvoiceRequest::payer_metadata
        /// [`Bolt12Invoice::verify`]: crate::offers::invoice::Bolt12Invoice::verify
        /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
-       pub fn request_invoice_deriving_payer_id<'a, 'b, ES: Deref, T: secp256k1::Signing>(
-               &'a self, expanded_key: &ExpandedKey, entropy_source: ES, secp_ctx: &'b Secp256k1<T>,
+       pub fn request_invoice_deriving_payer_id<
+               'a, 'b, ES: Deref,
+               #[cfg(not(c_bindings))]
+               T: secp256k1::Signing
+       >(
+               &'a $self, expanded_key: &ExpandedKey, entropy_source: ES,
+               #[cfg(not(c_bindings))]
+               secp_ctx: &'b Secp256k1<T>,
+               #[cfg(c_bindings)]
+               secp_ctx: &'b Secp256k1<secp256k1::All>,
                payment_id: PaymentId
-       ) -> Result<InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T>, Bolt12SemanticError>
+       ) -> Result<$builder, Bolt12SemanticError>
        where
                ES::Target: EntropySource,
        {
-               if self.offer_features().requires_unknown_bits() {
+               if $self.offer_features().requires_unknown_bits() {
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
 
-               Ok(InvoiceRequestBuilder::deriving_payer_id(
-                       self, expanded_key, entropy_source, secp_ctx, payment_id
-               ))
+               Ok(<$builder>::deriving_payer_id($self, expanded_key, entropy_source, secp_ctx, payment_id))
        }
+} }
 
+macro_rules! request_invoice_explicit_payer_id { ($self: ident, $builder: ty) => {
        /// Similar to [`Offer::request_invoice_deriving_payer_id`] except uses `payer_id` for the
        /// [`InvoiceRequest::payer_id`] instead of deriving a different key for each request.
        ///
        /// Useful for recurring payments using the same `payer_id` with different invoices.
        ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
        /// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id
        pub fn request_invoice_deriving_metadata<ES: Deref>(
-               &self, payer_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
+               &$self, payer_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
                payment_id: PaymentId
-       ) -> Result<InvoiceRequestBuilder<ExplicitPayerId, secp256k1::SignOnly>, Bolt12SemanticError>
+       ) -> Result<$builder, Bolt12SemanticError>
        where
                ES::Target: EntropySource,
        {
-               if self.offer_features().requires_unknown_bits() {
+               if $self.offer_features().requires_unknown_bits() {
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
 
-               Ok(InvoiceRequestBuilder::deriving_metadata(
-                       self, payer_id, expanded_key, entropy_source, payment_id
-               ))
+               Ok(<$builder>::deriving_metadata($self, payer_id, expanded_key, entropy_source, payment_id))
        }
 
        /// Creates an [`InvoiceRequestBuilder`] for the offer with the given `metadata` and `payer_id`,
@@ -559,20 +672,32 @@ impl Offer {
        ///
        /// Errors if the offer contains unknown required features.
        ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
        /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
        pub fn request_invoice(
-               &self, metadata: Vec<u8>, payer_id: PublicKey
-       ) -> Result<InvoiceRequestBuilder<ExplicitPayerId, secp256k1::SignOnly>, Bolt12SemanticError> {
-               if self.offer_features().requires_unknown_bits() {
+               &$self, metadata: Vec<u8>, payer_id: PublicKey
+       ) -> Result<$builder, Bolt12SemanticError> {
+               if $self.offer_features().requires_unknown_bits() {
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
 
-               Ok(InvoiceRequestBuilder::new(self, metadata, payer_id))
+               Ok(<$builder>::new($self, metadata, payer_id))
        }
+} }
 
-       #[cfg(test)]
+#[cfg(not(c_bindings))]
+impl Offer {
+       request_invoice_derived_payer_id!(self, InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T>);
+       request_invoice_explicit_payer_id!(self, InvoiceRequestBuilder<ExplicitPayerId, secp256k1::SignOnly>);
+}
+
+#[cfg(c_bindings)]
+impl Offer {
+       request_invoice_derived_payer_id!(self, InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b>);
+       request_invoice_explicit_payer_id!(self, InvoiceRequestWithExplicitPayerIdBuilder);
+}
+
+#[cfg(test)]
+impl Offer {
        pub(super) fn as_tlv_stream(&self) -> OfferTlvStreamRef {
                self.contents.as_tlv_stream()
        }
@@ -584,6 +709,20 @@ impl AsRef<[u8]> for Offer {
        }
 }
 
+impl PartialEq for Offer {
+       fn eq(&self, other: &Self) -> bool {
+               self.bytes.eq(&other.bytes)
+       }
+}
+
+impl Eq for Offer {}
+
+impl Hash for Offer {
+       fn hash<H: Hasher>(&self, state: &mut H) {
+               self.bytes.hash(state);
+       }
+}
+
 impl OfferContents {
        pub fn chains(&self) -> Vec<ChainHash> {
                self.chains.as_ref().cloned().unwrap_or_else(|| vec![self.implied_chain()])
@@ -914,7 +1053,15 @@ impl core::fmt::Display for Offer {
 
 #[cfg(test)]
 mod tests {
-       use super::{Amount, Offer, OfferBuilder, OfferTlvStreamRef, Quantity};
+       use super::{Amount, Offer, OfferTlvStreamRef, Quantity};
+       #[cfg(not(c_bindings))]
+       use {
+               super::OfferBuilder,
+       };
+       #[cfg(c_bindings)]
+       use {
+               super::OfferWithExplicitMetadataBuilder as OfferBuilder,
+       };
 
        use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::network::constants::Network;
@@ -1043,6 +1190,8 @@ mod tests {
                let entropy = FixedEntropy {};
                let secp_ctx = Secp256k1::new();
 
+               #[cfg(c_bindings)]
+               use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
                let offer = OfferBuilder
                        ::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
                        .amount_msats(1000)
@@ -1099,6 +1248,8 @@ mod tests {
                        ],
                };
 
+               #[cfg(c_bindings)]
+               use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
                let offer = OfferBuilder
                        ::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
                        .amount_msats(1000)
@@ -1153,8 +1304,13 @@ mod tests {
                assert_eq!(tlv_stream.amount, Some(1000));
                assert_eq!(tlv_stream.currency, None);
 
+               #[cfg(not(c_bindings))]
                let builder = OfferBuilder::new("foo".into(), pubkey(42))
                        .amount(currency_amount.clone());
+               #[cfg(c_bindings)]
+               let mut builder = OfferBuilder::new("foo".into(), pubkey(42));
+               #[cfg(c_bindings)]
+               builder.amount(currency_amount.clone());
                let tlv_stream = builder.offer.as_tlv_stream();
                assert_eq!(builder.offer.amount, Some(currency_amount.clone()));
                assert_eq!(tlv_stream.amount, Some(10));
index ba3ab1d1ef3bad508bc14ab4e7b933c00269d7e3..42177960868ab553ee1385f2569146dc8209e2bf 100644 (file)
@@ -85,6 +85,7 @@ use bitcoin::blockdata::constants::ChainHash;
 use bitcoin::network::constants::Network;
 use bitcoin::secp256k1::{PublicKey, Secp256k1, self};
 use core::convert::TryFrom;
+use core::hash::{Hash, Hasher};
 use core::ops::Deref;
 use core::str::FromStr;
 use core::time::Duration;
@@ -96,7 +97,7 @@ use crate::ln::channelmanager::PaymentId;
 use crate::ln::features::InvoiceRequestFeatures;
 use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
 use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
-use crate::offers::invoice::{BlindedPayInfo, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder};
+use crate::offers::invoice::BlindedPayInfo;
 use crate::offers::invoice_request::{InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
 use crate::offers::offer::{OfferTlvStream, OfferTlvStreamRef};
 use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
@@ -105,6 +106,15 @@ use crate::offers::signer::{Metadata, MetadataMaterial, self};
 use crate::util::ser::{SeekReadable, WithoutLength, Writeable, Writer};
 use crate::util::string::PrintableString;
 
+#[cfg(not(c_bindings))]
+use {
+       crate::offers::invoice::{DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder},
+};
+#[cfg(c_bindings)]
+use {
+       crate::offers::invoice::{InvoiceWithDerivedSigningPubkeyBuilder, InvoiceWithExplicitSigningPubkeyBuilder},
+};
+
 use crate::prelude::*;
 
 #[cfg(feature = "std")]
@@ -124,7 +134,18 @@ pub struct RefundBuilder<'a, T: secp256k1::Signing> {
        secp_ctx: Option<&'a Secp256k1<T>>,
 }
 
-impl<'a> RefundBuilder<'a, secp256k1::SignOnly> {
+/// Builds a [`Refund`] for the "offer for money" flow.
+///
+/// See [module-level documentation] for usage.
+///
+/// [module-level documentation]: self
+#[cfg(c_bindings)]
+pub struct RefundMaybeWithDerivedMetadataBuilder<'a> {
+       refund: RefundContents,
+       secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
+}
+
+macro_rules! refund_explicit_metadata_builder_methods { () => {
        /// Creates a new builder for a refund using the [`Refund::payer_id`] for the public node id to
        /// send to if no [`Refund::paths`] are set. Otherwise, it may be a transient pubkey.
        ///
@@ -155,9 +176,11 @@ impl<'a> RefundBuilder<'a, secp256k1::SignOnly> {
                        secp_ctx: None,
                })
        }
-}
+} }
 
-impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
+macro_rules! refund_builder_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr, $secp_context: ty $(, $self_mut: tt)?
+) => {
        /// Similar to [`RefundBuilder::new`] except, if [`RefundBuilder::path`] is called, the payer id
        /// is derived from the given [`ExpandedKey`] and nonce. This provides sender privacy by using a
        /// different payer id for each refund, assuming a different nonce is used.  Otherwise, the
@@ -173,7 +196,7 @@ impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
        /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
        pub fn deriving_payer_id<ES: Deref>(
                description: String, node_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
-               secp_ctx: &'a Secp256k1<T>, amount_msats: u64, payment_id: PaymentId
+               secp_ctx: &'a Secp256k1<$secp_context>, amount_msats: u64, payment_id: PaymentId
        ) -> Result<Self, Bolt12SemanticError> where ES::Target: EntropySource {
                if amount_msats > MAX_VALUE_MSAT {
                        return Err(Bolt12SemanticError::InvalidAmount);
@@ -197,17 +220,17 @@ impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
        /// already passed is valid and can be checked for using [`Refund::is_expired`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn absolute_expiry(mut self, absolute_expiry: Duration) -> Self {
-               self.refund.absolute_expiry = Some(absolute_expiry);
-               self
+       pub fn absolute_expiry($($self_mut)* $self: $self_type, absolute_expiry: Duration) -> $return_type {
+               $self.refund.absolute_expiry = Some(absolute_expiry);
+               $return_value
        }
 
        /// Sets the [`Refund::issuer`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn issuer(mut self, issuer: String) -> Self {
-               self.refund.issuer = Some(issuer);
-               self
+       pub fn issuer($($self_mut)* $self: $self_type, issuer: String) -> $return_type {
+               $self.refund.issuer = Some(issuer);
+               $return_value
        }
 
        /// Adds a blinded path to [`Refund::paths`]. Must include at least one path if only connected
@@ -215,26 +238,26 @@ impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
        ///
        /// Successive calls to this method will add another blinded path. Caller is responsible for not
        /// adding duplicate paths.
-       pub fn path(mut self, path: BlindedPath) -> Self {
-               self.refund.paths.get_or_insert_with(Vec::new).push(path);
-               self
+       pub fn path($($self_mut)* $self: $self_type, path: BlindedPath) -> $return_type {
+               $self.refund.paths.get_or_insert_with(Vec::new).push(path);
+               $return_value
        }
 
        /// Sets the [`Refund::chain`] of the given [`Network`] for paying an invoice. If not
        /// called, [`Network::Bitcoin`] is assumed.
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn chain(self, network: Network) -> Self {
-               self.chain_hash(ChainHash::using_genesis_block(network))
+       pub fn chain($self: $self_type, network: Network) -> $return_type {
+               $self.chain_hash(ChainHash::using_genesis_block(network))
        }
 
        /// Sets the [`Refund::chain`] of the given [`ChainHash`] for paying an invoice. If not called,
        /// [`Network::Bitcoin`] is assumed.
        ///
        /// Successive calls to this method will override the previous setting.
-       pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Self {
-               self.refund.chain = Some(chain);
-               self
+       pub(crate) fn chain_hash($($self_mut)* $self: $self_type, chain: ChainHash) -> $return_type {
+               $self.refund.chain = Some(chain);
+               $return_value
        }
 
        /// Sets [`Refund::quantity`] of items. This is purely for informational purposes. It is useful
@@ -246,65 +269,109 @@ impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        /// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity
        /// [`Offer`]: crate::offers::offer::Offer
-       pub fn quantity(mut self, quantity: u64) -> Self {
-               self.refund.quantity = Some(quantity);
-               self
+       pub fn quantity($($self_mut)* $self: $self_type, quantity: u64) -> $return_type {
+               $self.refund.quantity = Some(quantity);
+               $return_value
        }
 
        /// Sets the [`Refund::payer_note`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn payer_note(mut self, payer_note: String) -> Self {
-               self.refund.payer_note = Some(payer_note);
-               self
+       pub fn payer_note($($self_mut)* $self: $self_type, payer_note: String) -> $return_type {
+               $self.refund.payer_note = Some(payer_note);
+               $return_value
        }
 
        /// Builds a [`Refund`] after checking for valid semantics.
-       pub fn build(mut self) -> Result<Refund, Bolt12SemanticError> {
-               if self.refund.chain() == self.refund.implied_chain() {
-                       self.refund.chain = None;
+       pub fn build($($self_mut)* $self: $self_type) -> Result<Refund, Bolt12SemanticError> {
+               if $self.refund.chain() == $self.refund.implied_chain() {
+                       $self.refund.chain = None;
                }
 
                // Create the metadata for stateless verification of a Bolt12Invoice.
-               if self.refund.payer.0.has_derivation_material() {
-                       let mut metadata = core::mem::take(&mut self.refund.payer.0);
+               if $self.refund.payer.0.has_derivation_material() {
+                       let mut metadata = core::mem::take(&mut $self.refund.payer.0);
 
-                       if self.refund.paths.is_none() {
+                       if $self.refund.paths.is_none() {
                                metadata = metadata.without_keys();
                        }
 
-                       let mut tlv_stream = self.refund.as_tlv_stream();
+                       let mut tlv_stream = $self.refund.as_tlv_stream();
                        tlv_stream.0.metadata = None;
                        if metadata.derives_payer_keys() {
                                tlv_stream.2.payer_id = None;
                        }
 
-                       let (derived_metadata, keys) = metadata.derive_from(tlv_stream, self.secp_ctx);
+                       let (derived_metadata, keys) = metadata.derive_from(tlv_stream, $self.secp_ctx);
                        metadata = derived_metadata;
                        if let Some(keys) = keys {
-                               self.refund.payer_id = keys.public_key();
+                               $self.refund.payer_id = keys.public_key();
                        }
 
-                       self.refund.payer.0 = metadata;
+                       $self.refund.payer.0 = metadata;
                }
 
                let mut bytes = Vec::new();
-               self.refund.write(&mut bytes).unwrap();
+               $self.refund.write(&mut bytes).unwrap();
+
+               Ok(Refund {
+                       bytes,
+                       #[cfg(not(c_bindings))]
+                       contents: $self.refund,
+                       #[cfg(c_bindings)]
+                       contents: $self.refund.clone(),
+               })
+       }
+} }
+
+#[cfg(test)]
+macro_rules! refund_builder_test_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
+) => {
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       pub(crate) fn clear_paths($($self_mut)* $self: $self_type) -> $return_type {
+               $self.refund.paths = None;
+               $return_value
+       }
 
-               Ok(Refund { bytes, contents: self.refund })
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       fn features_unchecked($($self_mut)* $self: $self_type, features: InvoiceRequestFeatures) -> $return_type {
+               $self.refund.features = features;
+               $return_value
        }
+} }
+
+impl<'a> RefundBuilder<'a, secp256k1::SignOnly> {
+       refund_explicit_metadata_builder_methods!();
 }
 
-#[cfg(test)]
 impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
-       pub(crate) fn clear_paths(mut self) -> Self {
-               self.refund.paths = None;
-               self
-       }
+       refund_builder_methods!(self, Self, Self, self, T, mut);
+
+       #[cfg(test)]
+       refund_builder_test_methods!(self, Self, Self, self, mut);
+}
 
-       fn features_unchecked(mut self, features: InvoiceRequestFeatures) -> Self {
-               self.refund.features = features;
-               self
+#[cfg(all(c_bindings, not(test)))]
+impl<'a> RefundMaybeWithDerivedMetadataBuilder<'a> {
+       refund_explicit_metadata_builder_methods!();
+       refund_builder_methods!(self, &mut Self, (), (), secp256k1::All);
+}
+
+#[cfg(all(c_bindings, test))]
+impl<'a> RefundMaybeWithDerivedMetadataBuilder<'a> {
+       refund_explicit_metadata_builder_methods!();
+       refund_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All);
+       refund_builder_test_methods!(self, &mut Self, &mut Self, self);
+}
+
+#[cfg(c_bindings)]
+impl<'a> From<RefundBuilder<'a, secp256k1::All>>
+for RefundMaybeWithDerivedMetadataBuilder<'a> {
+       fn from(builder: RefundBuilder<'a, secp256k1::All>) -> Self {
+               let RefundBuilder { refund, secp_ctx } = builder;
+
+               Self { refund, secp_ctx }
        }
 }
 
@@ -317,7 +384,6 @@ impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
 /// [`Offer`]: crate::offers::offer::Offer
 #[derive(Clone, Debug)]
-#[cfg_attr(test, derive(PartialEq))]
 pub struct Refund {
        pub(super) bytes: Vec<u8>,
        pub(super) contents: RefundContents,
@@ -423,7 +489,9 @@ impl Refund {
        pub fn payer_note(&self) -> Option<PrintableString> {
                self.contents.payer_note()
        }
+}
 
+macro_rules! respond_with_explicit_signing_pubkey_methods { ($self: ident, $builder: ty) => {
        /// Creates an [`InvoiceBuilder`] for the refund with the given required fields and using the
        /// [`Duration`] since [`std::time::SystemTime::UNIX_EPOCH`] as the creation time.
        ///
@@ -435,14 +503,14 @@ impl Refund {
        /// [`Duration`]: core::time::Duration
        #[cfg(feature = "std")]
        pub fn respond_with(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
                signing_pubkey: PublicKey,
-       ) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
+       ) -> Result<$builder, Bolt12SemanticError> {
                let created_at = std::time::SystemTime::now()
                        .duration_since(std::time::SystemTime::UNIX_EPOCH)
                        .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
 
-               self.respond_with_no_std(payment_paths, payment_hash, signing_pubkey, created_at)
+               $self.respond_with_no_std(payment_paths, payment_hash, signing_pubkey, created_at)
        }
 
        /// Creates an [`InvoiceBuilder`] for the refund with the given required fields.
@@ -468,16 +536,18 @@ impl Refund {
        ///
        /// [`Bolt12Invoice::created_at`]: crate::offers::invoice::Bolt12Invoice::created_at
        pub fn respond_with_no_std(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
                signing_pubkey: PublicKey, created_at: Duration
-       ) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
-               if self.features().requires_unknown_bits() {
+       ) -> Result<$builder, Bolt12SemanticError> {
+               if $self.features().requires_unknown_bits() {
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
 
-               InvoiceBuilder::for_refund(self, payment_paths, created_at, payment_hash, signing_pubkey)
+               <$builder>::for_refund($self, payment_paths, created_at, payment_hash, signing_pubkey)
        }
+} }
 
+macro_rules! respond_with_derived_signing_pubkey_methods { ($self: ident, $builder: ty) => {
        /// Creates an [`InvoiceBuilder`] for the refund using the given required fields and that uses
        /// derived signing keys to sign the [`Bolt12Invoice`].
        ///
@@ -488,9 +558,9 @@ impl Refund {
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        #[cfg(feature = "std")]
        pub fn respond_using_derived_keys<ES: Deref>(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
                expanded_key: &ExpandedKey, entropy_source: ES
-       ) -> Result<InvoiceBuilder<DerivedSigningPubkey>, Bolt12SemanticError>
+       ) -> Result<$builder, Bolt12SemanticError>
        where
                ES::Target: EntropySource,
        {
@@ -498,7 +568,7 @@ impl Refund {
                        .duration_since(std::time::SystemTime::UNIX_EPOCH)
                        .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
 
-               self.respond_using_derived_keys_no_std(
+               $self.respond_using_derived_keys_no_std(
                        payment_paths, payment_hash, created_at, expanded_key, entropy_source
                )
        }
@@ -512,22 +582,36 @@ impl Refund {
        ///
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        pub fn respond_using_derived_keys_no_std<ES: Deref>(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
                created_at: core::time::Duration, expanded_key: &ExpandedKey, entropy_source: ES
-       ) -> Result<InvoiceBuilder<DerivedSigningPubkey>, Bolt12SemanticError>
+       ) -> Result<$builder, Bolt12SemanticError>
        where
                ES::Target: EntropySource,
        {
-               if self.features().requires_unknown_bits() {
+               if $self.features().requires_unknown_bits() {
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
 
                let nonce = Nonce::from_entropy_source(entropy_source);
                let keys = signer::derive_keys(nonce, expanded_key);
-               InvoiceBuilder::for_refund_using_keys(self, payment_paths, created_at, payment_hash, keys)
+               <$builder>::for_refund_using_keys($self, payment_paths, created_at, payment_hash, keys)
        }
+} }
 
-       #[cfg(test)]
+#[cfg(not(c_bindings))]
+impl Refund {
+       respond_with_explicit_signing_pubkey_methods!(self, InvoiceBuilder<ExplicitSigningPubkey>);
+       respond_with_derived_signing_pubkey_methods!(self, InvoiceBuilder<DerivedSigningPubkey>);
+}
+
+#[cfg(c_bindings)]
+impl Refund {
+       respond_with_explicit_signing_pubkey_methods!(self, InvoiceWithExplicitSigningPubkeyBuilder);
+       respond_with_derived_signing_pubkey_methods!(self, InvoiceWithDerivedSigningPubkeyBuilder);
+}
+
+#[cfg(test)]
+impl Refund {
        fn as_tlv_stream(&self) -> RefundTlvStreamRef {
                self.contents.as_tlv_stream()
        }
@@ -539,6 +623,20 @@ impl AsRef<[u8]> for Refund {
        }
 }
 
+impl PartialEq for Refund {
+       fn eq(&self, other: &Self) -> bool {
+               self.bytes.eq(&other.bytes)
+       }
+}
+
+impl Eq for Refund {}
+
+impl Hash for Refund {
+       fn hash<H: Hasher>(&self, state: &mut H) {
+               self.bytes.hash(state);
+       }
+}
+
 impl RefundContents {
        pub fn description(&self) -> PrintableString {
                PrintableString(&self.description)
@@ -783,7 +881,15 @@ impl core::fmt::Display for Refund {
 
 #[cfg(test)]
 mod tests {
-       use super::{Refund, RefundBuilder, RefundTlvStreamRef};
+       use super::{Refund, RefundTlvStreamRef};
+       #[cfg(not(c_bindings))]
+       use {
+               super::RefundBuilder,
+       };
+       #[cfg(c_bindings)]
+       use {
+               super::RefundMaybeWithDerivedMetadataBuilder as RefundBuilder,
+       };
 
        use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::network::constants::Network;
index 39122472eacb5513640f7fd78020a10ea783957d..29bed53d83f561f030e4966373c4403020ac6ffc 100644 (file)
@@ -11,7 +11,7 @@
 
 use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey};
 use bitcoin::secp256k1::schnorr::Signature;
-use core::convert::{AsRef, Infallible};
+use core::convert::AsRef; 
 use core::time::Duration;
 use crate::blinded_path::{BlindedHop, BlindedPath};
 use crate::sign::EntropySource;
@@ -20,12 +20,16 @@ use crate::ln::features::BlindedHopFeatures;
 use crate::offers::invoice::BlindedPayInfo;
 use crate::offers::merkle::TaggedHash;
 
+pub(crate) fn fail_sign<T: AsRef<TaggedHash>>(_message: &T) -> Result<Signature, ()> {
+       Err(())
+}
+
 pub(crate) fn payer_keys() -> KeyPair {
        let secp_ctx = Secp256k1::new();
        KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap())
 }
 
-pub(crate) fn payer_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, Infallible> {
+pub(crate) fn payer_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, ()> {
        let secp_ctx = Secp256k1::new();
        let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
        Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
@@ -40,7 +44,7 @@ pub(crate) fn recipient_keys() -> KeyPair {
        KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap())
 }
 
-pub(crate) fn recipient_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, Infallible> {
+pub(crate) fn recipient_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, ()> {
        let secp_ctx = Secp256k1::new();
        let keys = KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap());
        Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
index 2dc5a89a37b5fae41071d8c4589d813b39916a3e..9f6c5cb435276ffbc4a76793f1f9fea624986de4 100644 (file)
@@ -28,7 +28,7 @@ use super::packet::OnionMessageContents;
 use super::packet::ParsedOnionMessageContents;
 use super::offers::OffersMessageHandler;
 use super::packet::{BIG_PACKET_HOP_DATA_LEN, ForwardControlTlvs, Packet, Payload, ReceiveControlTlvs, SMALL_PACKET_HOP_DATA_LEN};
-use crate::util::logger::Logger;
+use crate::util::logger::{Logger, WithContext};
 use crate::util::ser::Writeable;
 
 use core::fmt;
@@ -358,16 +358,28 @@ where
                const MIN_PEER_CHANNELS: usize = 3;
 
                let network_graph = self.network_graph.deref().read_only();
-               let paths = peers.iter()
+               let is_recipient_announced =
+                       network_graph.nodes().contains_key(&NodeId::from_pubkey(&recipient));
+
+               let mut peer_info = peers.iter()
                        // Limit to peers with announced channels
-                       .filter(|pubkey|
+                       .filter_map(|pubkey|
                                network_graph
                                        .node(&NodeId::from_pubkey(pubkey))
-                                       .map(|info| &info.channels[..])
-                                       .map(|channels| channels.len() >= MIN_PEER_CHANNELS)
-                                       .unwrap_or(false)
+                                       .filter(|info| info.channels.len() >= MIN_PEER_CHANNELS)
+                                       .map(|info| (*pubkey, info.is_tor_only(), info.channels.len()))
                        )
-                       .map(|pubkey| vec![*pubkey, recipient])
+                       // Exclude Tor-only nodes when the recipient is announced.
+                       .filter(|(_, is_tor_only, _)| !(*is_tor_only && is_recipient_announced))
+                       .collect::<Vec<_>>();
+
+               // Prefer using non-Tor nodes with the most channels as the introduction node.
+               peer_info.sort_unstable_by(|(_, a_tor_only, a_channels), (_, b_tor_only, b_channels)| {
+                       a_tor_only.cmp(b_tor_only).then(a_channels.cmp(b_channels).reverse())
+               });
+
+               let paths = peer_info.into_iter()
+                       .map(|(pubkey, _, _)| vec![pubkey, recipient])
                        .map(|node_pks| BlindedPath::new_for_message(&node_pks, &*self.entropy_source, secp_ctx))
                        .take(MAX_PATHS)
                        .collect::<Result<Vec<_>, _>>();
@@ -375,7 +387,7 @@ where
                match paths {
                        Ok(paths) if !paths.is_empty() => Ok(paths),
                        _ => {
-                               if network_graph.nodes().contains_key(&NodeId::from_pubkey(&recipient)) {
+                               if is_recipient_announced {
                                        BlindedPath::one_hop_for_message(recipient, &*self.entropy_source, secp_ctx)
                                                .map(|path| vec![path])
                                } else {
@@ -736,25 +748,31 @@ where
                &self, contents: T, destination: Destination, reply_path: Option<BlindedPath>,
                log_suffix: fmt::Arguments
        ) -> Result<SendSuccess, SendError> {
+               let mut logger = WithContext::from(&self.logger, None, None);
                let result = self.find_path(destination)
-                       .and_then(|path| self.enqueue_onion_message(path, contents, reply_path, log_suffix));
+                       .and_then(|path| {
+                               let first_hop = path.intermediate_nodes.get(0).map(|p| *p);
+                               logger = WithContext::from(&self.logger, first_hop, None);
+                               self.enqueue_onion_message(path, contents, reply_path, log_suffix)
+                       });
 
                match result.as_ref() {
                        Err(SendError::GetNodeIdFailed) => {
-                               log_warn!(self.logger, "Unable to retrieve node id {}", log_suffix);
+                               log_warn!(logger, "Unable to retrieve node id {}", log_suffix);
                        },
                        Err(SendError::PathNotFound) => {
-                               log_trace!(self.logger, "Failed to find path {}", log_suffix);
+                               log_trace!(logger, "Failed to find path {}", log_suffix);
                        },
                        Err(e) => {
-                               log_trace!(self.logger, "Failed sending onion message {}: {:?}", log_suffix, e);
+                               log_trace!(logger, "Failed sending onion message {}: {:?}", log_suffix, e);
                        },
                        Ok(SendSuccess::Buffered) => {
-                               log_trace!(self.logger, "Buffered onion message {}", log_suffix);
+                               log_trace!(logger, "Buffered onion message {}", log_suffix);
                        },
                        Ok(SendSuccess::BufferedAwaitingConnection(node_id)) => {
                                log_trace!(
-                                       self.logger, "Buffered onion message waiting on peer connection {}: {:?}",
+                                       logger,
+                                       "Buffered onion message waiting on peer connection {}: {}",
                                        log_suffix, node_id
                                );
                        },
@@ -913,12 +931,13 @@ where
        OMH::Target: OffersMessageHandler,
        CMH::Target: CustomOnionMessageHandler,
 {
-       fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &OnionMessage) {
+       fn handle_onion_message(&self, peer_node_id: &PublicKey, msg: &OnionMessage) {
+               let logger = WithContext::from(&self.logger, Some(*peer_node_id), None);
                match self.peel_onion_message(msg) {
                        Ok(PeeledOnion::Receive(message, path_id, reply_path)) => {
                                log_trace!(
-                                       self.logger,
-                                  "Received an onion message with path_id {:02x?} and {} reply_path: {:?}",
+                                       logger,
+                                       "Received an onion message with path_id {:02x?} and {} reply_path: {:?}",
                                        path_id, if reply_path.is_some() { "a" } else { "no" }, message);
 
                                match message {
@@ -945,7 +964,10 @@ where
                        Ok(PeeledOnion::Forward(next_node_id, onion_message)) => {
                                let mut message_recipients = self.message_recipients.lock().unwrap();
                                if outbound_buffer_full(&next_node_id, &message_recipients) {
-                                       log_trace!(self.logger, "Dropping forwarded onion message to peer {:?}: outbound buffer full", next_node_id);
+                                       log_trace!(
+                                               logger,
+                                               "Dropping forwarded onion message to peer {}: outbound buffer full",
+                                               next_node_id);
                                        return
                                }
 
@@ -959,16 +981,19 @@ where
                                                e.get(), OnionMessageRecipient::ConnectedPeer(..)
                                        ) => {
                                                e.get_mut().enqueue_message(onion_message);
-                                               log_trace!(self.logger, "Forwarding an onion message to peer {}", next_node_id);
+                                               log_trace!(logger, "Forwarding an onion message to peer {}", next_node_id);
                                        },
                                        _ => {
-                                               log_trace!(self.logger, "Dropping forwarded onion message to disconnected peer {:?}", next_node_id);
+                                               log_trace!(
+                                                       logger,
+                                                       "Dropping forwarded onion message to disconnected peer {}",
+                                                       next_node_id);
                                                return
                                        },
                                }
                        },
                        Err(e) => {
-                               log_error!(self.logger, "Failed to process onion message {:?}", e);
+                               log_error!(logger, "Failed to process onion message {:?}", e);
                        }
                }
        }
index 281586edd36591f3bda4227fd70972d87c99a531..045772486ba7aa150636a1e7b5420a606e87ce64 100644 (file)
@@ -1241,6 +1241,18 @@ pub struct NodeInfo {
        pub announcement_info: Option<NodeAnnouncementInfo>
 }
 
+impl NodeInfo {
+       /// Returns whether the node has only announced Tor addresses.
+       pub fn is_tor_only(&self) -> bool {
+               self.announcement_info
+                       .as_ref()
+                       .map(|info| info.addresses())
+                       .and_then(|addresses| (!addresses.is_empty()).then(|| addresses))
+                       .map(|addresses| addresses.iter().all(|address| address.is_tor()))
+                       .unwrap_or(false)
+       }
+}
+
 impl fmt::Display for NodeInfo {
        fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
                write!(f, " channels: {:?}, announcement_info: {:?}",
@@ -2099,6 +2111,7 @@ pub(crate) mod tests {
        use crate::ln::chan_utils::make_funding_redeemscript;
        #[cfg(feature = "std")]
        use crate::ln::features::InitFeatures;
+       use crate::ln::msgs::SocketAddress;
        use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate, NodeAlias, MAX_EXCESS_BYTES_FOR_RELAY, NodeId, RoutingFees, ChannelUpdateInfo, ChannelInfo, NodeAnnouncementInfo, NodeInfo};
        use crate::routing::utxo::{UtxoLookupError, UtxoResult};
        use crate::ln::msgs::{RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement,
@@ -2106,7 +2119,7 @@ pub(crate) mod tests {
                ReplyChannelRange, QueryChannelRange, QueryShortChannelIds, MAX_VALUE_MSAT};
        use crate::util::config::UserConfig;
        use crate::util::test_utils;
-       use crate::util::ser::{ReadableArgs, Readable, Writeable};
+       use crate::util::ser::{Hostname, ReadableArgs, Readable, Writeable};
        use crate::util::scid_utils::scid_from_parts;
 
        use crate::routing::gossip::REMOVED_ENTRIES_TRACKING_AGE_LIMIT_SECS;
@@ -3484,6 +3497,112 @@ pub(crate) mod tests {
                let node_id = NodeId([42; 33]);
                assert_eq!(format!("{}", &node_id), "2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a");
        }
+
+       #[test]
+       fn is_tor_only_node() {
+               let network_graph = create_network_graph();
+               let (secp_ctx, gossip_sync) = create_gossip_sync(&network_graph);
+
+               let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
+               let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
+               let node_1_id = NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, node_1_privkey));
+
+               let announcement = get_signed_channel_announcement(|_| {}, node_1_privkey, node_2_privkey, &secp_ctx);
+               gossip_sync.handle_channel_announcement(&announcement).unwrap();
+
+               let tcp_ip_v4 = SocketAddress::TcpIpV4 {
+                       addr: [255, 254, 253, 252],
+                       port: 9735
+               };
+               let tcp_ip_v6 = SocketAddress::TcpIpV6 {
+                       addr: [255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240],
+                       port: 9735
+               };
+               let onion_v2 = SocketAddress::OnionV2([255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 38, 7]);
+               let onion_v3 = SocketAddress::OnionV3 {
+                       ed25519_pubkey: [255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 238, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 225, 224],
+                       checksum: 32,
+                       version: 16,
+                       port: 9735
+               };
+               let hostname = SocketAddress::Hostname {
+                       hostname: Hostname::try_from(String::from("host")).unwrap(),
+                       port: 9735,
+               };
+
+               assert!(!network_graph.read_only().node(&node_1_id).unwrap().is_tor_only());
+
+               let announcement = get_signed_node_announcement(|_| {}, node_1_privkey, &secp_ctx);
+               gossip_sync.handle_node_announcement(&announcement).unwrap();
+               assert!(!network_graph.read_only().node(&node_1_id).unwrap().is_tor_only());
+
+               let announcement = get_signed_node_announcement(
+                       |announcement| {
+                               announcement.addresses = vec![
+                                       tcp_ip_v4.clone(), tcp_ip_v6.clone(), onion_v2.clone(), onion_v3.clone(),
+                                       hostname.clone()
+                               ];
+                               announcement.timestamp += 1000;
+                       },
+                       node_1_privkey, &secp_ctx
+               );
+               gossip_sync.handle_node_announcement(&announcement).unwrap();
+               assert!(!network_graph.read_only().node(&node_1_id).unwrap().is_tor_only());
+
+               let announcement = get_signed_node_announcement(
+                       |announcement| {
+                               announcement.addresses = vec![
+                                       tcp_ip_v4.clone(), tcp_ip_v6.clone(), onion_v2.clone(), onion_v3.clone()
+                               ];
+                               announcement.timestamp += 2000;
+                       },
+                       node_1_privkey, &secp_ctx
+               );
+               gossip_sync.handle_node_announcement(&announcement).unwrap();
+               assert!(!network_graph.read_only().node(&node_1_id).unwrap().is_tor_only());
+
+               let announcement = get_signed_node_announcement(
+                       |announcement| {
+                               announcement.addresses = vec![
+                                       tcp_ip_v6.clone(), onion_v2.clone(), onion_v3.clone()
+                               ];
+                               announcement.timestamp += 3000;
+                       },
+                       node_1_privkey, &secp_ctx
+               );
+               gossip_sync.handle_node_announcement(&announcement).unwrap();
+               assert!(!network_graph.read_only().node(&node_1_id).unwrap().is_tor_only());
+
+               let announcement = get_signed_node_announcement(
+                       |announcement| {
+                               announcement.addresses = vec![onion_v2.clone(), onion_v3.clone()];
+                               announcement.timestamp += 4000;
+                       },
+                       node_1_privkey, &secp_ctx
+               );
+               gossip_sync.handle_node_announcement(&announcement).unwrap();
+               assert!(network_graph.read_only().node(&node_1_id).unwrap().is_tor_only());
+
+               let announcement = get_signed_node_announcement(
+                       |announcement| {
+                               announcement.addresses = vec![onion_v2.clone()];
+                               announcement.timestamp += 5000;
+                       },
+                       node_1_privkey, &secp_ctx
+               );
+               gossip_sync.handle_node_announcement(&announcement).unwrap();
+               assert!(network_graph.read_only().node(&node_1_id).unwrap().is_tor_only());
+
+               let announcement = get_signed_node_announcement(
+                       |announcement| {
+                               announcement.addresses = vec![tcp_ip_v4.clone()];
+                               announcement.timestamp += 6000;
+                       },
+                       node_1_privkey, &secp_ctx
+               );
+               gossip_sync.handle_node_announcement(&announcement).unwrap();
+               assert!(!network_graph.read_only().node(&node_1_id).unwrap().is_tor_only());
+       }
 }
 
 #[cfg(ldk_bench)]
index 8e59c9bd46f5ff5edeed994adb96d2667a0acfc3..8d37182266b11debdda0d96787358c36c87dcd59 100644 (file)
@@ -986,7 +986,7 @@ impl Payee {
                        _ => None,
                }
        }
-       fn blinded_route_hints(&self) -> &[(BlindedPayInfo, BlindedPath)] {
+       pub(crate) fn blinded_route_hints(&self) -> &[(BlindedPayInfo, BlindedPath)] {
                match self {
                        Self::Blinded { route_hints, .. } => &route_hints[..],
                        Self::Clear { .. } => &[]
index 69581e5e20a42e0c8ca79338cd2fc476f092f075..6c3d1ec42cb41dbc618c002c15eee4cf190f809a 100644 (file)
@@ -15,10 +15,10 @@ extern crate possiblyrandom;
 
 #[cfg(not(feature = "hashbrown"))]
 mod std_hashtables {
-       pub use std::collections::HashMap;
        pub use std::collections::hash_map::RandomState;
+       pub use std::collections::HashMap;
 
-       pub(crate) use std::collections::{HashSet, hash_map};
+       pub(crate) use std::collections::{hash_map, HashSet};
 
        pub(crate) type OccupiedHashMapEntry<'a, K, V> =
                std::collections::hash_map::OccupiedEntry<'a, K, V>;
@@ -26,20 +26,32 @@ mod std_hashtables {
                std::collections::hash_map::VacantEntry<'a, K, V>;
 
        /// Builds a new [`HashMap`].
-       pub fn new_hash_map<K, V>() -> HashMap<K, V> { HashMap::new() }
+       pub fn new_hash_map<K, V>() -> HashMap<K, V> {
+               HashMap::new()
+       }
        /// Builds a new [`HashMap`] with the given capacity.
        pub fn hash_map_with_capacity<K, V>(cap: usize) -> HashMap<K, V> {
                HashMap::with_capacity(cap)
        }
-       pub(crate) fn hash_map_from_iter<K: core::hash::Hash + Eq, V, I: IntoIterator<Item=(K, V)>>(iter: I) -> HashMap<K, V> {
+       pub(crate) fn hash_map_from_iter<
+               K: core::hash::Hash + Eq,
+               V,
+               I: IntoIterator<Item = (K, V)>,
+       >(
+               iter: I,
+       ) -> HashMap<K, V> {
                HashMap::from_iter(iter)
        }
 
-       pub(crate) fn new_hash_set<K>() -> HashSet<K> { HashSet::new() }
+       pub(crate) fn new_hash_set<K>() -> HashSet<K> {
+               HashSet::new()
+       }
        pub(crate) fn hash_set_with_capacity<K>(cap: usize) -> HashSet<K> {
                HashSet::with_capacity(cap)
        }
-       pub(crate) fn hash_set_from_iter<K: core::hash::Hash + Eq, I: IntoIterator<Item=K>>(iter: I) -> HashSet<K> {
+       pub(crate) fn hash_set_from_iter<K: core::hash::Hash + Eq, I: IntoIterator<Item = K>>(
+               iter: I,
+       ) -> HashSet<K> {
                HashSet::from_iter(iter)
        }
 }
@@ -64,7 +76,8 @@ mod hashbrown_tables {
                /// A simple implementation of [`BuildHasher`] that uses `getrandom` to opportunistically
                /// randomize, if the platform supports it.
                pub struct RandomState {
-                       k0: u64, k1: u64,
+                       k0: u64,
+                       k1: u64,
                }
 
                impl RandomState {
@@ -72,7 +85,8 @@ mod hashbrown_tables {
                        /// target platform.
                        pub fn new() -> RandomState {
                                let (k0, k1);
-                               #[cfg(all(not(fuzzing), feature = "possiblyrandom"))] {
+                               #[cfg(all(not(fuzzing), feature = "possiblyrandom"))]
+                               {
                                        let mut keys = [0; 16];
                                        possiblyrandom::getpossiblyrandom(&mut keys);
 
@@ -83,7 +97,8 @@ mod hashbrown_tables {
                                        k0 = u64::from_le_bytes(k0_bytes);
                                        k1 = u64::from_le_bytes(k1_bytes);
                                }
-                               #[cfg(any(fuzzing, not(feature = "possiblyrandom")))] {
+                               #[cfg(any(fuzzing, not(feature = "possiblyrandom")))]
+                               {
                                        k0 = 0;
                                        k1 = 0;
                                }
@@ -92,7 +107,9 @@ mod hashbrown_tables {
                }
 
                impl Default for RandomState {
-                       fn default() -> RandomState { RandomState::new() }
+                       fn default() -> RandomState {
+                               RandomState::new()
+                       }
                }
 
                impl BuildHasher for RandomState {
@@ -103,8 +120,8 @@ mod hashbrown_tables {
                }
        }
 
-       pub use hasher::*;
        use super::*;
+       pub use hasher::*;
 
        /// The HashMap type used in LDK.
        pub type HashMap<K, V> = hashbrown::HashMap<K, V, RandomState>;
@@ -123,7 +140,13 @@ mod hashbrown_tables {
        pub fn hash_map_with_capacity<K, V>(cap: usize) -> HashMap<K, V> {
                HashMap::with_capacity_and_hasher(cap, RandomState::new())
        }
-       pub(crate) fn hash_map_from_iter<K: core::hash::Hash + Eq, V, I: IntoIterator<Item=(K, V)>>(iter: I) -> HashMap<K, V> {
+       pub(crate) fn hash_map_from_iter<
+               K: core::hash::Hash + Eq,
+               V,
+               I: IntoIterator<Item = (K, V)>,
+       >(
+               iter: I,
+       ) -> HashMap<K, V> {
                let iter = iter.into_iter();
                let min_size = iter.size_hint().0;
                let mut res = HashMap::with_capacity_and_hasher(min_size, RandomState::new());
@@ -137,7 +160,9 @@ mod hashbrown_tables {
        pub(crate) fn hash_set_with_capacity<K>(cap: usize) -> HashSet<K> {
                HashSet::with_capacity_and_hasher(cap, RandomState::new())
        }
-       pub(crate) fn hash_set_from_iter<K: core::hash::Hash + Eq, I: IntoIterator<Item=K>>(iter: I) -> HashSet<K> {
+       pub(crate) fn hash_set_from_iter<K: core::hash::Hash + Eq, I: IntoIterator<Item = K>>(
+               iter: I,
+       ) -> HashSet<K> {
                let iter = iter.into_iter();
                let min_size = iter.size_hint().0;
                let mut res = HashSet::with_capacity_and_hasher(min_size, RandomState::new());
index c24a99f8f97109dde673a59eb7fbb28200a6e3b9..7447c92d4be8b61abc23b0527d458796687bd29e 100644 (file)
@@ -847,6 +847,7 @@ impl Readable for Vec<u8> {
 impl_for_vec!(ecdsa::Signature);
 impl_for_vec!(crate::chain::channelmonitor::ChannelMonitorUpdate);
 impl_for_vec!(crate::ln::channelmanager::MonitorUpdateCompletionAction);
+impl_for_vec!(crate::ln::msgs::SocketAddress);
 impl_for_vec!((A, B), A, B);
 impl_writeable_for_vec!(&crate::routing::router::BlindedTail);
 impl_readable_for_vec!(crate::routing::router::BlindedTail);