Merge pull request #1266 from TheBlueMatt/2022-01-fix-double-fail-panic
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Thu, 17 Feb 2022 03:41:50 +0000 (03:41 +0000)
committerGitHub <noreply@github.com>
Thu, 17 Feb 2022 03:41:50 +0000 (03:41 +0000)
Fix a debug panic caused by receiving MPP parts after a failure

24 files changed:
.github/workflows/build.yml
README.md
fuzz/src/chanmon_consistency.rs
fuzz/src/full_stack.rs
lightning-background-processor/src/lib.rs
lightning-invoice/src/lib.rs
lightning-invoice/src/utils.rs
lightning-persister/Cargo.toml
lightning-persister/src/lib.rs
lightning/Cargo.toml
lightning/src/chain/keysinterface.rs
lightning/src/lib.rs
lightning/src/ln/channel.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_test_utils.rs
lightning/src/ln/msgs.rs
lightning/src/ln/onion_utils.rs
lightning/src/ln/peer_channel_encryptor.rs
lightning/src/routing/network_graph.rs
lightning/src/routing/router.rs
lightning/src/util/crypto.rs [new file with mode: 0644]
lightning/src/util/mod.rs
lightning/src/util/scid_utils.rs
lightning/src/util/test_utils.rs

index fef042beee6a79f7c2e5812890b698641e093834..c982060a8c5cab14c7d077ff123314e4607d91ab 100644 (file)
@@ -231,7 +231,7 @@ jobs:
           cd ..
       - name: Run benchmarks on Rust ${{ matrix.toolchain }}
         run: |
-          cargo bench --features unstable
+          cargo bench --features _bench_unstable
 
   check_commits:
     runs-on: ubuntu-latest
index ea1f08ae165e0ecbb3b1a49c15ab209bf43ab7e9..dad0b39241576094ed5fe45d601d000bd0c3516d 100644 (file)
--- a/README.md
+++ b/README.md
@@ -30,8 +30,8 @@ sufficient for a developer or project to experiment with it. Recent increased
 contribution rate to the project is expected to lead to a high quality, stable,
 production-worthy implementation in 2021.
 
-Communications for Rust-Lightning and Lightning Development Kit happens through
-[LDK slack](http://lightningdevkit.org/).
+Communications for Rust-Lightning and Lightning Development Kit happens through our LDK
+[slack](https://join.slack.com/t/lightningdevkit/shared_invite/zt-tte36cb7-r5f41MDn3ObFtDu~N9dCrQ) & [discord](https://discord.gg/5AcknnMfBw) channels.
 
 Crates
 -----------
index 8c11ba2c6c712aba66604a62861416dbd3a551e5..79faba901581190742232aafe994fd969a5e0332 100644 (file)
@@ -34,7 +34,7 @@ use lightning::chain::{BestBlock, ChannelMonitorUpdateErr, chainmonitor, channel
 use lightning::chain::channelmonitor::{ChannelMonitor, MonitorEvent};
 use lightning::chain::transaction::OutPoint;
 use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
-use lightning::chain::keysinterface::{KeyMaterial, KeysInterface, InMemorySigner};
+use lightning::chain::keysinterface::{KeyMaterial, KeysInterface, InMemorySigner, Recipient};
 use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
 use lightning::ln::channelmanager::{ChainParameters, ChannelManager, PaymentSendFailure, ChannelManagerReadArgs};
 use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
@@ -161,8 +161,8 @@ struct KeyProvider {
 impl KeysInterface for KeyProvider {
        type Signer = EnforcingSigner;
 
-       fn get_node_secret(&self) -> SecretKey {
-               SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, self.node_id]).unwrap()
+       fn get_node_secret(&self, _recipient: Recipient) -> Result<SecretKey, ()> {
+               Ok(SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, self.node_id]).unwrap())
        }
 
        fn get_inbound_payment_key_material(&self) -> KeyMaterial {
@@ -188,7 +188,7 @@ impl KeysInterface for KeyProvider {
                let id = self.rand_bytes_id.fetch_add(1, atomic::Ordering::Relaxed);
                let keys = InMemorySigner::new(
                        &secp_ctx,
-                       self.get_node_secret(),
+                       self.get_node_secret(Recipient::Node).unwrap(),
                        SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, self.node_id]).unwrap(),
                        SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, self.node_id]).unwrap(),
                        SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, self.node_id]).unwrap(),
@@ -212,7 +212,7 @@ impl KeysInterface for KeyProvider {
        fn read_chan_signer(&self, buffer: &[u8]) -> Result<Self::Signer, DecodeError> {
                let mut reader = std::io::Cursor::new(buffer);
 
-               let inner: InMemorySigner = ReadableArgs::read(&mut reader, self.get_node_secret())?;
+               let inner: InMemorySigner = ReadableArgs::read(&mut reader, self.get_node_secret(Recipient::Node).unwrap())?;
                let state = self.make_enforcement_state_cell(inner.commitment_seed);
 
                Ok(EnforcingSigner {
@@ -222,7 +222,7 @@ impl KeysInterface for KeyProvider {
                })
        }
 
-       fn sign_invoice(&self, _hrp_bytes: &[u8], _invoice_data: &[u5]) -> Result<RecoverableSignature, ()> {
+       fn sign_invoice(&self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient) -> Result<RecoverableSignature, ()> {
                unreachable!()
        }
 }
index 9db04b645e4143496a921eac2cd4a1d0bc83ae37..19ec541b942592a00ba93fe1687eba2e95559952 100644 (file)
@@ -31,7 +31,7 @@ use lightning::chain::{BestBlock, Confirm, Listen};
 use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
 use lightning::chain::chainmonitor;
 use lightning::chain::transaction::OutPoint;
-use lightning::chain::keysinterface::{InMemorySigner, KeyMaterial, KeysInterface};
+use lightning::chain::keysinterface::{InMemorySigner, Recipient, KeyMaterial, KeysInterface};
 use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
 use lightning::ln::channelmanager::{ChainParameters, ChannelManager};
 use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor,IgnoringMessageHandler};
@@ -265,8 +265,8 @@ struct KeyProvider {
 impl KeysInterface for KeyProvider {
        type Signer = EnforcingSigner;
 
-       fn get_node_secret(&self) -> SecretKey {
-               self.node_secret.clone()
+       fn get_node_secret(&self, _recipient: Recipient) -> Result<SecretKey, ()> {
+               Ok(self.node_secret.clone())
        }
 
        fn get_inbound_payment_key_material(&self) -> KeyMaterial {
@@ -336,7 +336,7 @@ impl KeysInterface for KeyProvider {
                ))
        }
 
-       fn sign_invoice(&self, _hrp_bytes: &[u8], _invoice_data: &[u5]) -> Result<RecoverableSignature, ()> {
+       fn sign_invoice(&self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient) -> Result<RecoverableSignature, ()> {
                unreachable!()
        }
 }
@@ -390,7 +390,8 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
                best_block: BestBlock::from_genesis(network),
        };
        let channelmanager = Arc::new(ChannelManager::new(fee_est.clone(), monitor.clone(), broadcast.clone(), Arc::clone(&logger), keys_manager.clone(), config, params));
-       let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret());
+       keys_manager.counter.fetch_sub(1, Ordering::AcqRel);
+       let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret(Recipient::Node).unwrap());
        let network_graph = Arc::new(NetworkGraph::new(genesis_block(network).block_hash()));
        let net_graph_msg_handler = Arc::new(NetGraphMsgHandler::new(Arc::clone(&network_graph), None, Arc::clone(&logger)));
        let scorer = FixedPenaltyScorer::with_penalty(0);
index 6e5e60e5014f207374fdca8bbb8ddf87db027a72..8ed0014a72dbc94fda3dd8e2d311120ee23e234f 100644 (file)
@@ -343,7 +343,7 @@ mod tests {
        use bitcoin::network::constants::Network;
        use lightning::chain::{BestBlock, Confirm, chainmonitor};
        use lightning::chain::channelmonitor::ANTI_REORG_DELAY;
-       use lightning::chain::keysinterface::{InMemorySigner, KeysInterface, KeysManager};
+       use lightning::chain::keysinterface::{InMemorySigner, Recipient, KeysInterface, KeysManager};
        use lightning::chain::transaction::OutPoint;
        use lightning::get_event_msg;
        use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, ChannelManager, SimpleArcChannelManager};
@@ -426,7 +426,7 @@ mod tests {
                        let network_graph = Arc::new(NetworkGraph::new(genesis_block.header.block_hash()));
                        let net_graph_msg_handler = Some(Arc::new(NetGraphMsgHandler::new(network_graph.clone(), Some(chain_source.clone()), logger.clone())));
                        let msg_handler = MessageHandler { chan_handler: Arc::new(test_utils::TestChannelMessageHandler::new()), route_handler: Arc::new(test_utils::TestRoutingMessageHandler::new() )};
-                       let peer_manager = Arc::new(PeerManager::new(msg_handler, keys_manager.get_node_secret(), &seed, logger.clone(), IgnoringMessageHandler{}));
+                       let peer_manager = Arc::new(PeerManager::new(msg_handler, keys_manager.get_node_secret(Recipient::Node).unwrap(), &seed, logger.clone(), IgnoringMessageHandler{}));
                        let node = Node { node: manager, net_graph_msg_handler, peer_manager, chain_monitor, persister, tx_broadcaster, network_graph, logger, best_block };
                        nodes.push(node);
                }
index c44db77575dec86ebe2e51ff9b432d20ce5f53ed..d676f6c28d6faf7926fa970030ffb1d258fa1af7 100644 (file)
@@ -1387,6 +1387,12 @@ pub enum CreationError {
 
        /// The supplied millisatoshi amount was greater than the total bitcoin supply.
        InvalidAmount,
+
+       /// Route hints were required for this invoice and were missing. Applies to
+       /// [phantom invoices].
+       ///
+       /// [phantom invoices]: crate::utils::create_phantom_invoice
+       MissingRouteHints,
 }
 
 impl Display for CreationError {
@@ -1396,6 +1402,7 @@ impl Display for CreationError {
                        CreationError::RouteTooLong => f.write_str("The specified route has too many hops and can't be encoded"),
                        CreationError::TimestampOutOfBounds => f.write_str("The Unix timestamp of the supplied date is less than zero or greater than 35-bits"),
                        CreationError::InvalidAmount => f.write_str("The supplied millisatoshi amount was greater than the total bitcoin supply"),
+                       CreationError::MissingRouteHints => f.write_str("The invoice required route hints and they weren't provided"),
                }
        }
 }
index ef95b3e355b5abd1c91303e3e708b2014041482a..fc82541a564e8cbd82c64f75fd37d4fa87ecd8af 100644 (file)
@@ -8,9 +8,9 @@ use bitcoin_hashes::Hash;
 use crate::prelude::*;
 use lightning::chain;
 use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
-use lightning::chain::keysinterface::{Sign, KeysInterface};
+use lightning::chain::keysinterface::{Recipient, KeysInterface, Sign};
 use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
-use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, MIN_FINAL_CLTV_EXPIRY};
+use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY, MIN_CLTV_EXPIRY_DELTA};
 use lightning::ln::msgs::LightningError;
 use lightning::routing::scoring::Score;
 use lightning::routing::network_graph::{NetworkGraph, RoutingFees};
@@ -21,6 +21,99 @@ use core::convert::TryInto;
 use core::ops::Deref;
 use core::time::Duration;
 
+#[cfg(feature = "std")]
+/// Utility to create an invoice that can be paid to one of multiple nodes, or a "phantom invoice."
+/// See [`PhantomKeysManager`] for more information on phantom node payments.
+///
+/// `phantom_route_hints` parameter:
+/// * Contains channel info for all nodes participating in the phantom invoice
+/// * Entries are retrieved from a call to [`ChannelManager::get_phantom_route_hints`] on each
+///   participating node
+/// * It is fine to cache `phantom_route_hints` and reuse it across invoices, as long as the data is
+///   updated when a channel becomes disabled or closes
+/// * Note that if too many channels are included in [`PhantomRouteHints::channels`], the invoice
+///   may be too long for QR code scanning. To fix this, `PhantomRouteHints::channels` may be pared
+///   down
+///
+/// `payment_hash` and `payment_secret` come from [`ChannelManager::create_inbound_payment`] or
+/// [`ChannelManager::create_inbound_payment_for_hash`]. These values can be retrieved from any
+/// participating node.
+///
+/// Note that the provided `keys_manager`'s `KeysInterface` implementation must support phantom
+/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
+/// requirement).
+///
+/// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
+/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints
+/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
+pub fn create_phantom_invoice<Signer: Sign, K: Deref>(
+       amt_msat: Option<u64>, description: String, payment_hash: PaymentHash, payment_secret:
+       PaymentSecret, phantom_route_hints: Vec<PhantomRouteHints>, keys_manager: K, network: Currency
+) -> Result<Invoice, SignOrCreationError<()>> where K::Target: KeysInterface {
+       if phantom_route_hints.len() == 0 {
+               return Err(SignOrCreationError::CreationError(CreationError::MissingRouteHints))
+       }
+       let mut invoice = InvoiceBuilder::new(network)
+               .description(description)
+               .current_timestamp()
+               .payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
+               .payment_secret(payment_secret)
+               .min_final_cltv_expiry(MIN_FINAL_CLTV_EXPIRY.into());
+       if let Some(amt) = amt_msat {
+               invoice = invoice.amount_milli_satoshis(amt);
+       }
+
+       for hint in phantom_route_hints {
+               for channel in &hint.channels {
+                       let short_channel_id = match channel.short_channel_id {
+                               Some(id) => id,
+                               None => continue,
+                       };
+                       let forwarding_info = match &channel.counterparty.forwarding_info {
+                               Some(info) => info.clone(),
+                               None => continue,
+                       };
+                       invoice = invoice.private_route(RouteHint(vec![
+                                       RouteHintHop {
+                                               src_node_id: channel.counterparty.node_id,
+                                               short_channel_id,
+                                               fees: RoutingFees {
+                                                       base_msat: forwarding_info.fee_base_msat,
+                                                       proportional_millionths: forwarding_info.fee_proportional_millionths,
+                                               },
+                                               cltv_expiry_delta: forwarding_info.cltv_expiry_delta,
+                                               htlc_minimum_msat: None,
+                                               htlc_maximum_msat: None,
+                                       },
+                                       RouteHintHop {
+                                               src_node_id: hint.real_node_pubkey,
+                                               short_channel_id: hint.phantom_scid,
+                                               fees: RoutingFees {
+                                                       base_msat: 0,
+                                                       proportional_millionths: 0,
+                                               },
+                                               cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA,
+                                               htlc_minimum_msat: None,
+                                               htlc_maximum_msat: None,
+                                       }])
+                       );
+               }
+       }
+
+       let raw_invoice = match invoice.build_raw() {
+               Ok(inv) => inv,
+               Err(e) => return Err(SignOrCreationError::CreationError(e))
+       };
+       let hrp_str = raw_invoice.hrp.to_string();
+       let hrp_bytes = hrp_str.as_bytes();
+       let data_without_signature = raw_invoice.data.to_base32();
+       let signed_raw_invoice = raw_invoice.sign(|_| keys_manager.sign_invoice(hrp_bytes, &data_without_signature, Recipient::PhantomNode));
+       match signed_raw_invoice {
+               Ok(inv) => Ok(Invoice::from_signed(inv).unwrap()),
+               Err(e) => Err(SignOrCreationError::SignError(e))
+       }
+}
+
 #[cfg(feature = "std")]
 /// Utility to construct an invoice. Generally, unless you want to do something like a custom
 /// cltv_expiry, this is what you should be using to create an invoice. The reason being, this
@@ -118,7 +211,7 @@ where
        let hrp_str = raw_invoice.hrp.to_string();
        let hrp_bytes = hrp_str.as_bytes();
        let data_without_signature = raw_invoice.data.to_base32();
-       let signed_raw_invoice = raw_invoice.sign(|_| keys_manager.sign_invoice(hrp_bytes, &data_without_signature));
+       let signed_raw_invoice = raw_invoice.sign(|_| keys_manager.sign_invoice(hrp_bytes, &data_without_signature, Recipient::Node));
        match signed_raw_invoice {
                Ok(inv) => Ok(Invoice::from_signed(inv).unwrap()),
                Err(e) => Err(SignOrCreationError::SignError(e))
@@ -192,13 +285,17 @@ where
 mod test {
        use core::time::Duration;
        use {Currency, Description, InvoiceDescription};
-       use lightning::ln::PaymentHash;
+       use bitcoin_hashes::Hash;
+       use bitcoin_hashes::sha256::Hash as Sha256;
+       use lightning::chain::keysinterface::PhantomKeysManager;
+       use lightning::ln::{PaymentPreimage, PaymentHash};
        use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY;
        use lightning::ln::functional_test_utils::*;
        use lightning::ln::features::InitFeatures;
        use lightning::ln::msgs::ChannelMessageHandler;
        use lightning::routing::router::{PaymentParameters, RouteParameters, find_route};
-       use lightning::util::events::MessageSendEventsProvider;
+       use lightning::util::enforcing_trait_impls::EnforcingSigner;
+       use lightning::util::events::{MessageSendEvent, MessageSendEventsProvider, Event};
        use lightning::util::test_utils;
        use utils::create_invoice_from_channelmanager_and_duration_since_epoch;
 
@@ -254,4 +351,121 @@ mod test {
                let events = nodes[1].node.get_and_clear_pending_msg_events();
                assert_eq!(events.len(), 2);
        }
+
+       #[test]
+       #[cfg(feature = "std")]
+       fn test_multi_node_receive() {
+               do_test_multi_node_receive(true);
+               do_test_multi_node_receive(false);
+       }
+
+       #[cfg(feature = "std")]
+       fn do_test_multi_node_receive(user_generated_pmt_hash: bool) {
+               let mut chanmon_cfgs = create_chanmon_cfgs(3);
+               let seed_1 = [42 as u8; 32];
+               let seed_2 = [43 as u8; 32];
+               let cross_node_seed = [44 as u8; 32];
+               chanmon_cfgs[1].keys_manager.backing = PhantomKeysManager::new(&seed_1, 43, 44, &cross_node_seed);
+               chanmon_cfgs[2].keys_manager.backing = PhantomKeysManager::new(&seed_2, 43, 44, &cross_node_seed);
+               let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+               let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+               let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+               let chan_0_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001, InitFeatures::known(), InitFeatures::known());
+               nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1.1);
+               nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1.0);
+               let chan_0_2 = create_announced_chan_between_nodes_with_value(&nodes, 0, 2, 100000, 10001, InitFeatures::known(), InitFeatures::known());
+               nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_0_2.1);
+               nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
+
+               let payment_amt = 10_000;
+               let (payment_preimage, payment_hash, payment_secret) = {
+                       if user_generated_pmt_hash {
+                               let payment_preimage = PaymentPreimage([1; 32]);
+                               let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner());
+                               let payment_secret = nodes[1].node.create_inbound_payment_for_hash(payment_hash, Some(payment_amt), 3600).unwrap();
+                               (payment_preimage, payment_hash, payment_secret)
+                       } else {
+                               let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(payment_amt), 3600).unwrap();
+                               let payment_preimage = nodes[1].node.get_payment_preimage(payment_hash, payment_secret).unwrap();
+                               (payment_preimage, payment_hash, payment_secret)
+                       }
+               };
+               let route_hints = vec![
+                       nodes[1].node.get_phantom_route_hints(),
+                       nodes[2].node.get_phantom_route_hints(),
+               ];
+               let invoice = ::utils::create_phantom_invoice::<EnforcingSigner, &test_utils::TestKeysInterface>(Some(payment_amt), "test".to_string(), payment_hash, payment_secret, route_hints, &nodes[1].keys_manager, Currency::BitcoinTestnet).unwrap();
+
+               assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
+               assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
+               assert_eq!(invoice.route_hints().len(), 2);
+               assert!(!invoice.features().unwrap().supports_basic_mpp());
+
+               let payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key())
+                       .with_features(invoice.features().unwrap().clone())
+                       .with_route_hints(invoice.route_hints());
+               let params = RouteParameters {
+                       payment_params,
+                       final_value_msat: invoice.amount_milli_satoshis().unwrap(),
+                       final_cltv_expiry_delta: invoice.min_final_cltv_expiry() as u32,
+               };
+               let first_hops = nodes[0].node.list_usable_channels();
+               let network_graph = node_cfgs[0].network_graph;
+               let logger = test_utils::TestLogger::new();
+               let scorer = test_utils::TestScorer::with_penalty(0);
+               let route = find_route(
+                       &nodes[0].node.get_our_node_id(), &params, network_graph,
+                       Some(&first_hops.iter().collect::<Vec<_>>()), &logger, &scorer,
+               ).unwrap();
+               let (payment_event, fwd_idx) = {
+                       let mut payment_hash = PaymentHash([0; 32]);
+                       payment_hash.0.copy_from_slice(&invoice.payment_hash().as_ref()[0..32]);
+                       nodes[0].node.send_payment(&route, payment_hash, &Some(invoice.payment_secret().clone())).unwrap();
+                       let mut added_monitors = nodes[0].chain_monitor.added_monitors.lock().unwrap();
+                       assert_eq!(added_monitors.len(), 1);
+                       added_monitors.clear();
+
+                       let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+                       assert_eq!(events.len(), 1);
+                       let fwd_idx = match events[0] {
+                               MessageSendEvent::UpdateHTLCs { node_id, .. } => {
+                                       if node_id == nodes[1].node.get_our_node_id() {
+                                               1
+                                       } else { 2 }
+                               },
+                               _ => panic!("Unexpected event")
+                       };
+                       (SendEvent::from_event(events.remove(0)), fwd_idx)
+               };
+               nodes[fwd_idx].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
+               commitment_signed_dance!(nodes[fwd_idx], nodes[0], &payment_event.commitment_msg, false, true);
+
+               // Note that we have to "forward pending HTLCs" twice before we see the PaymentReceived as
+               // this "emulates" the payment taking two hops, providing some privacy to make phantom node
+               // payments "look real" by taking more time.
+               expect_pending_htlcs_forwardable_ignore!(nodes[fwd_idx]);
+               nodes[fwd_idx].node.process_pending_htlc_forwards();
+               expect_pending_htlcs_forwardable_ignore!(nodes[fwd_idx]);
+               nodes[fwd_idx].node.process_pending_htlc_forwards();
+
+               let payment_preimage_opt = if user_generated_pmt_hash { None } else { Some(payment_preimage) };
+               expect_payment_received!(&nodes[fwd_idx], payment_hash, payment_secret, payment_amt, payment_preimage_opt);
+               do_claim_payment_along_route(&nodes[0], &vec!(&vec!(&nodes[fwd_idx])[..]), false, payment_preimage);
+               let events = nodes[0].node.get_and_clear_pending_events();
+               assert_eq!(events.len(), 2);
+               match events[0] {
+                       Event::PaymentSent { payment_preimage: ref ev_preimage, payment_hash: ref ev_hash, ref fee_paid_msat, .. } => {
+                               assert_eq!(payment_preimage, *ev_preimage);
+                               assert_eq!(payment_hash, *ev_hash);
+                               assert_eq!(fee_paid_msat, &Some(0));
+                       },
+                       _ => panic!("Unexpected event")
+               }
+               match events[1] {
+                       Event::PaymentPathSuccessful { payment_hash: hash, .. } => {
+                               assert_eq!(hash, Some(payment_hash));
+                       },
+                       _ => panic!("Unexpected event")
+               }
+       }
 }
index 27f772cf326e84574bfd2cbfd70c6c9360d84ccf..79ffeeeb281ad1e28ef190212c5185b56225f4a3 100644 (file)
@@ -9,7 +9,7 @@ Utilities to manage Rust-Lightning channel data persistence and retrieval.
 """
 
 [features]
-unstable = ["lightning/unstable"]
+_bench_unstable = ["lightning/_bench_unstable"]
 
 [dependencies]
 bitcoin = "0.27"
index b853b5796b6e7e226fb2efae1d00495ef5de389d..558f4b8fe3cee9d56b75b2230f203ccca6608e9c 100644 (file)
@@ -3,8 +3,8 @@
 #![deny(broken_intra_doc_links)]
 #![deny(missing_docs)]
 
-#![cfg_attr(all(test, feature = "unstable"), feature(test))]
-#[cfg(all(test, feature = "unstable"))] extern crate test;
+#![cfg_attr(all(test, feature = "_bench_unstable"), feature(test))]
+#[cfg(all(test, feature = "_bench_unstable"))] extern crate test;
 
 mod util;
 
@@ -362,7 +362,7 @@ mod tests {
        }
 }
 
-#[cfg(all(test, feature = "unstable"))]
+#[cfg(all(test, feature = "_bench_unstable"))]
 pub mod bench {
        use test::Bencher;
 
index 135d42a6a81ced0a147775ca9e352d8dcaf0fdc8..c5d0c5564afb9249784276c2df2567a57ebcf084 100644 (file)
@@ -24,7 +24,7 @@ max_level_trace = []
 # Allow signing of local transactions that may have been revoked or will be revoked, for functional testing (e.g. justice tx handling).
 # This is unsafe to use in production because it may result in the counterparty publishing taking our funds.
 unsafe_revoked_tx_signing = []
-unstable = []
+_bench_unstable = []
 
 no-std = ["hashbrown", "bitcoin/no-std", "core2/alloc"]
 std = ["bitcoin/std"]
index dffe060d91bb71a5f33e8c023de6cac94fb9826d..1daeec4ef62354a1fb9f4a5597ad7678a270ffe3 100644 (file)
@@ -31,6 +31,7 @@ use bitcoin::secp256k1::recovery::RecoverableSignature;
 use bitcoin::secp256k1;
 
 use util::{byte_utils, transaction_utils};
+use util::crypto::hkdf_extract_expand_twice;
 use util::ser::{Writeable, Writer, Readable, ReadableArgs};
 
 use chain::transaction::OutPoint;
@@ -379,15 +380,28 @@ pub trait BaseSign {
 pub trait Sign: BaseSign + Writeable + Clone {
 }
 
+/// Specifies the recipient of an invoice, to indicate to [`KeysInterface::sign_invoice`] what node
+/// secret key should be used to sign the invoice.
+pub enum Recipient {
+       /// The invoice should be signed with the local node secret key.
+       Node,
+       /// The invoice should be signed with the phantom node secret key. This secret key must be the
+       /// same for all nodes participating in the [phantom node payment].
+       ///
+       /// [phantom node payment]: PhantomKeysManager
+       PhantomNode,
+}
+
 /// A trait to describe an object which can get user secrets and key material.
 pub trait KeysInterface {
        /// A type which implements Sign which will be returned by get_channel_signer.
        type Signer : Sign;
 
-       /// Get node secret key (aka node_id or network_key).
+       /// Get node secret key (aka node_id or network_key) based on the provided [`Recipient`].
        ///
-       /// This method must return the same value each time it is called.
-       fn get_node_secret(&self) -> SecretKey;
+       /// This method must return the same value each time it is called with a given `Recipient`
+       /// parameter.
+       fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()>;
        /// Get a script pubkey which we send funds to when claiming on-chain contestable outputs.
        ///
        /// This method should return a different value each time it is called, to avoid linking
@@ -423,11 +437,22 @@ pub trait KeysInterface {
        /// this trait to parse the invoice and make sure they're signing what they expect, rather than
        /// blindly signing the hash.
        /// The hrp is ascii bytes, while the invoice data is base32.
-       fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5]) -> Result<RecoverableSignature, ()>;
+       ///
+       /// The secret key used to sign the invoice is dependent on the [`Recipient`].
+       fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], receipient: Recipient) -> Result<RecoverableSignature, ()>;
 
        /// Get secret key material as bytes for use in encrypting and decrypting inbound payment data.
        ///
+       /// If the implementor of this trait supports [phantom node payments], then every node that is
+       /// intended to be included in the phantom invoice route hints must return the same value from
+       /// this method.
+       //  This is because LDK avoids storing inbound payment data by encrypting payment data in the
+       //  payment hash and/or payment secret, therefore for a payment to be receivable by multiple
+       //  nodes, they must share the key that encrypts this payment data.
+       ///
        /// This method must return the same value each time it is called.
+       ///
+       /// [phantom node payments]: PhantomKeysManager
        fn get_inbound_payment_key_material(&self) -> KeyMaterial;
 }
 
@@ -810,6 +835,12 @@ impl ReadableArgs<SecretKey> for InMemorySigner {
 /// ChannelMonitor closes may use seed/1'
 /// Cooperative closes may use seed/2'
 /// The two close keys may be needed to claim on-chain funds!
+///
+/// This struct cannot be used for nodes that wish to support receiving phantom payments;
+/// [`PhantomKeysManager`] must be used instead.
+///
+/// Note that switching between this struct and [`PhantomKeysManager`] will invalidate any
+/// previously issued invoices and attempts to pay previous invoices will fail.
 pub struct KeysManager {
        secp_ctx: Secp256k1<secp256k1::All>,
        node_secret: SecretKey,
@@ -964,7 +995,7 @@ impl KeysManager {
        /// transaction will have a feerate, at least, of the given value.
        ///
        /// Returns `Err(())` if the output value is greater than the input value minus required fee,
-       /// if a descriptor was duplicated, or if an output descriptor script_pubkey 
+       /// if a descriptor was duplicated, or if an output descriptor `script_pubkey`
        /// does not match the one we can spend.
        ///
        /// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
@@ -1092,8 +1123,11 @@ impl KeysManager {
 impl KeysInterface for KeysManager {
        type Signer = InMemorySigner;
 
-       fn get_node_secret(&self) -> SecretKey {
-               self.node_secret.clone()
+       fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()> {
+               match recipient {
+                       Recipient::Node => Ok(self.node_secret.clone()),
+                       Recipient::PhantomNode => Err(())
+               }
        }
 
        fn get_inbound_payment_key_material(&self) -> KeyMaterial {
@@ -1130,12 +1164,116 @@ impl KeysInterface for KeysManager {
        }
 
        fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
-               InMemorySigner::read(&mut io::Cursor::new(reader), self.get_node_secret())
+               InMemorySigner::read(&mut io::Cursor::new(reader), self.node_secret.clone())
+       }
+
+       fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
+               let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data);
+               let secret = match recipient {
+                       Recipient::Node => self.get_node_secret(Recipient::Node)?,
+                       Recipient::PhantomNode => return Err(()),
+               };
+               Ok(self.secp_ctx.sign_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), &secret))
+       }
+}
+
+/// Similar to [`KeysManager`], but allows the node using this struct to receive phantom node
+/// payments.
+///
+/// A phantom node payment is a payment made to a phantom invoice, which is an invoice that can be
+/// paid to one of multiple nodes. This works because we encode the invoice route hints such that
+/// LDK will recognize an incoming payment as destined for a phantom node, and collect the payment
+/// itself without ever needing to forward to this fake node.
+///
+/// Phantom node payments are useful for load balancing between multiple LDK nodes. They also
+/// provide some fault tolerance, because payers will automatically retry paying other provided
+/// nodes in the case that one node goes down.
+///
+/// Note that multi-path payments are not supported in phantom invoices for security reasons.
+//  In the hypothetical case that we did support MPP phantom payments, there would be no way for
+//  nodes to know when the full payment has been received (and the preimage can be released) without
+//  significantly compromising on our safety guarantees. I.e., if we expose the ability for the user
+//  to tell LDK when the preimage can be released, we open ourselves to attacks where the preimage
+//  is released too early.
+//
+/// Switching between this struct and [`KeysManager`] will invalidate any previously issued
+/// invoices and attempts to pay previous invoices will fail.
+pub struct PhantomKeysManager {
+       inner: KeysManager,
+       inbound_payment_key: KeyMaterial,
+       phantom_secret: SecretKey,
+}
+
+impl KeysInterface for PhantomKeysManager {
+       type Signer = InMemorySigner;
+
+       fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()> {
+               match recipient {
+                       Recipient::Node => self.inner.get_node_secret(Recipient::Node),
+                       Recipient::PhantomNode => Ok(self.phantom_secret.clone()),
+               }
+       }
+
+       fn get_inbound_payment_key_material(&self) -> KeyMaterial {
+               self.inbound_payment_key.clone()
+       }
+
+       fn get_destination_script(&self) -> Script {
+               self.inner.get_destination_script()
+       }
+
+       fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
+               self.inner.get_shutdown_scriptpubkey()
+       }
+
+       fn get_channel_signer(&self, inbound: bool, channel_value_satoshis: u64) -> Self::Signer {
+               self.inner.get_channel_signer(inbound, channel_value_satoshis)
+       }
+
+       fn get_secure_random_bytes(&self) -> [u8; 32] {
+               self.inner.get_secure_random_bytes()
+       }
+
+       fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
+               self.inner.read_chan_signer(reader)
        }
 
-       fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5]) -> Result<RecoverableSignature, ()> {
+       fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
                let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data);
-               Ok(self.secp_ctx.sign_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), &self.get_node_secret()))
+               let secret = self.get_node_secret(recipient)?;
+               Ok(self.inner.secp_ctx.sign_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), &secret))
+       }
+}
+
+impl PhantomKeysManager {
+       /// Constructs a `PhantomKeysManager` given a 32-byte seed and an additional `cross_node_seed`
+       /// that is shared across all nodes that intend to participate in [phantom node payments] together.
+       ///
+       /// See [`KeysManager::new`] for more information on `seed`, `starting_time_secs`, and
+       /// `starting_time_nanos`.
+       ///
+       /// `cross_node_seed` must be the same across all phantom payment-receiving nodes and also the
+       /// same across restarts, or else inbound payments may fail.
+       ///
+       /// [phantom node payments]: PhantomKeysManager
+       pub fn new(seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32, cross_node_seed: &[u8; 32]) -> Self {
+               let inner = KeysManager::new(seed, starting_time_secs, starting_time_nanos);
+               let (inbound_key, phantom_key) = hkdf_extract_expand_twice(b"LDK Inbound and Phantom Payment Key Expansion", cross_node_seed);
+               Self {
+                       inner,
+                       inbound_payment_key: KeyMaterial(inbound_key),
+                       phantom_secret: SecretKey::from_slice(&phantom_key).unwrap(),
+               }
+       }
+
+       /// See [`KeysManager::spend_spendable_outputs`] for documentation on this method.
+       pub fn spend_spendable_outputs<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: Script, feerate_sat_per_1000_weight: u32, secp_ctx: &Secp256k1<C>) -> Result<Transaction, ()> {
+               self.inner.spend_spendable_outputs(descriptors, outputs, change_destination_script, feerate_sat_per_1000_weight, secp_ctx)
+       }
+
+       /// See [`KeysManager::derive_channel_keys`] for documentation on this method.
+       pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner {
+               self.inner.derive_channel_keys(channel_value_satoshis, params)
        }
 }
 
index ca18d7bb5cc0ba5ae64eadb5310e023d62b634b2..8fdf63ffad0e7f370359a9a3922490f939d05587 100644 (file)
@@ -30,8 +30,8 @@
 
 #![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
 
-#![cfg_attr(all(any(test, feature = "_test_utils"), feature = "unstable"), feature(test))]
-#[cfg(all(any(test, feature = "_test_utils"), feature = "unstable"))] extern crate test;
+#![cfg_attr(all(any(test, feature = "_test_utils"), feature = "_bench_unstable"), feature(test))]
+#[cfg(all(any(test, feature = "_test_utils"), feature = "_bench_unstable"))] extern crate test;
 
 #[cfg(not(any(feature = "std", feature = "no-std")))]
 compile_error!("at least one of the `std` or `no-std` features must be enabled");
index ed7975fe9ca9311794cbc036c5f38f5d8c3881de..fb9213142ae740e4b487786be3e50c7a9268807b 100644 (file)
@@ -6188,7 +6188,7 @@ mod tests {
        use ln::chan_utils::{ChannelPublicKeys, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters, htlc_success_tx_weight, htlc_timeout_tx_weight};
        use chain::BestBlock;
        use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
-       use chain::keysinterface::{InMemorySigner, KeyMaterial, KeysInterface, BaseSign};
+       use chain::keysinterface::{InMemorySigner, Recipient, KeyMaterial, KeysInterface, BaseSign};
        use chain::transaction::OutPoint;
        use util::config::UserConfig;
        use util::enforcing_trait_impls::EnforcingSigner;
@@ -6236,7 +6236,7 @@ mod tests {
        impl KeysInterface for Keys {
                type Signer = InMemorySigner;
 
-               fn get_node_secret(&self) -> SecretKey { panic!(); }
+               fn get_node_secret(&self, _recipient: Recipient) -> Result<SecretKey, ()> { panic!(); }
                fn get_inbound_payment_key_material(&self) -> KeyMaterial { panic!(); }
                fn get_destination_script(&self) -> Script {
                        let secp_ctx = Secp256k1::signing_only();
@@ -6256,7 +6256,7 @@ mod tests {
                }
                fn get_secure_random_bytes(&self) -> [u8; 32] { [0; 32] }
                fn read_chan_signer(&self, _data: &[u8]) -> Result<Self::Signer, DecodeError> { panic!(); }
-               fn sign_invoice(&self, _hrp_bytes: &[u8], _invoice_data: &[u5]) -> Result<RecoverableSignature, ()> { panic!(); }
+               fn sign_invoice(&self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient) -> Result<RecoverableSignature, ()> { panic!(); }
        }
 
        fn public_from_secret_hex(secp_ctx: &Secp256k1<All>, hex: &str) -> PublicKey {
index 0b24503cc92fc49c79d560cfe91e5f9c019c0fb8..8d15e0793d95523d8066f4042cdc0d6782ea2030 100644 (file)
@@ -24,10 +24,8 @@ use bitcoin::blockdata::constants::genesis_block;
 use bitcoin::network::constants::Network;
 
 use bitcoin::hashes::{Hash, HashEngine};
-use bitcoin::hashes::hmac::{Hmac, HmacEngine};
 use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::hashes::sha256d::Hash as Sha256dHash;
-use bitcoin::hashes::cmp::fixed_time_eq;
 use bitcoin::hash_types::{BlockHash, Txid};
 
 use bitcoin::secp256k1::key::{SecretKey,PublicKey};
@@ -50,12 +48,12 @@ use ln::msgs;
 use ln::msgs::NetAddress;
 use ln::onion_utils;
 use ln::msgs::{ChannelMessageHandler, DecodeError, LightningError, MAX_VALUE_MSAT, OptionalField};
-use chain::keysinterface::{Sign, KeysInterface, KeysManager, InMemorySigner};
+use chain::keysinterface::{Sign, KeysInterface, KeysManager, InMemorySigner, Recipient};
 use util::config::UserConfig;
 use util::events::{EventHandler, EventsProvider, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
 use util::{byte_utils, events};
+use util::scid_utils::fake_scid;
 use util::ser::{BigSize, FixedLengthReader, Readable, ReadableArgs, MaybeReadable, Writeable, Writer};
-use util::chacha20::{ChaCha20, ChaChaReader};
 use util::logger::{Level, Logger};
 use util::errors::APIError;
 
@@ -63,7 +61,7 @@ use io;
 use prelude::*;
 use core::{cmp, mem};
 use core::cell::RefCell;
-use io::{Cursor, Read};
+use io::Read;
 use sync::{Arc, Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard};
 use core::sync::atomic::{AtomicUsize, Ordering};
 use core::time::Duration;
@@ -84,6 +82,7 @@ mod inbound_payment {
        use ln::msgs;
        use ln::msgs::MAX_VALUE_MSAT;
        use util::chacha20::ChaCha20;
+       use util::crypto::hkdf_extract_expand_thrice;
        use util::logger::Logger;
 
        use core::convert::TryInto;
@@ -115,7 +114,13 @@ mod inbound_payment {
 
        impl ExpandedKey {
                pub(super) fn new(key_material: &KeyMaterial) -> ExpandedKey {
-                       hkdf_extract_expand(b"LDK Inbound Payment Key Expansion", &key_material)
+                       let (metadata_key, ldk_pmt_hash_key, user_pmt_hash_key) =
+                               hkdf_extract_expand_thrice(b"LDK Inbound Payment Key Expansion", &key_material.0);
+                       Self {
+                               metadata_key,
+                               ldk_pmt_hash_key,
+                               user_pmt_hash_key,
+                       }
                }
        }
 
@@ -333,31 +338,6 @@ mod inbound_payment {
                }
                return Ok(PaymentPreimage(decoded_payment_preimage))
        }
-
-       fn hkdf_extract_expand(salt: &[u8], ikm: &KeyMaterial) -> ExpandedKey {
-               let mut hmac = HmacEngine::<Sha256>::new(salt);
-               hmac.input(&ikm.0);
-               let prk = Hmac::from_engine(hmac).into_inner();
-               let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
-               hmac.input(&[1; 1]);
-               let metadata_key = Hmac::from_engine(hmac).into_inner();
-
-               let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
-               hmac.input(&metadata_key);
-               hmac.input(&[2; 1]);
-               let ldk_pmt_hash_key = Hmac::from_engine(hmac).into_inner();
-
-               let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
-               hmac.input(&ldk_pmt_hash_key);
-               hmac.input(&[3; 1]);
-               let user_pmt_hash_key = Hmac::from_engine(hmac).into_inner();
-
-               ExpandedKey {
-                       metadata_key,
-                       ldk_pmt_hash_key,
-                       user_pmt_hash_key,
-               }
-       }
 }
 
 // We hold various information about HTLC relay in the HTLC objects in Channel itself:
@@ -539,6 +519,12 @@ pub(super) enum HTLCFailReason {
        }
 }
 
+struct ReceiveError {
+       err_code: u16,
+       err_data: Vec<u8>,
+       msg: &'static str,
+}
+
 /// Return value for claim_funds_from_hop
 enum ClaimFundsFromHop {
        PrevHopForceClosed,
@@ -988,6 +974,13 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
 
        inbound_payment_key: inbound_payment::ExpandedKey,
 
+       /// LDK puts the [fake scids] that it generates into namespaces, to identify the type of an
+       /// incoming payment. To make it harder for a third-party to identify the type of a payment,
+       /// we encrypt the namespace identifier using these bytes.
+       ///
+       /// [fake scids]: crate::util::scid_utils::fake_scid
+       fake_scid_rand_bytes: [u8; 32],
+
        /// Used to track the last value sent in a node_announcement "timestamp" field. We ensure this
        /// value increases strictly since we don't assume access to a time source.
        last_node_announcement_serial: AtomicUsize,
@@ -1324,6 +1317,19 @@ pub enum PaymentSendFailure {
        },
 }
 
+/// Route hints used in constructing invoices for [phantom node payents].
+///
+/// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+pub struct PhantomRouteHints {
+       /// The list of channels to be included in the invoice route hints.
+       pub channels: Vec<ChannelDetails>,
+       /// A fake scid used for representing the phantom node's fake channel in generating the invoice
+       /// route hints.
+       pub phantom_scid: u64,
+       /// The pubkey of the real backing node that would ultimately receive the payment.
+       pub real_node_pubkey: PublicKey,
+}
+
 macro_rules! handle_error {
        ($self: ident, $internal: expr, $counterparty_node_id: expr) => {
                match $internal {
@@ -1700,11 +1706,12 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                        pending_inbound_payments: Mutex::new(HashMap::new()),
                        pending_outbound_payments: Mutex::new(HashMap::new()),
 
-                       our_network_key: keys_manager.get_node_secret(),
-                       our_network_pubkey: PublicKey::from_secret_key(&secp_ctx, &keys_manager.get_node_secret()),
+                       our_network_key: keys_manager.get_node_secret(Recipient::Node).unwrap(),
+                       our_network_pubkey: PublicKey::from_secret_key(&secp_ctx, &keys_manager.get_node_secret(Recipient::Node).unwrap()),
                        secp_ctx,
 
                        inbound_payment_key: expanded_inbound_key,
+                       fake_scid_rand_bytes: keys_manager.get_secure_random_bytes(),
 
                        last_node_announcement_serial: AtomicUsize::new(0),
                        highest_seen_timestamp: AtomicUsize::new(0),
@@ -2064,6 +2071,102 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                }
        }
 
+       fn construct_recv_pending_htlc_info(&self, hop_data: msgs::OnionHopData, shared_secret: [u8; 32],
+               payment_hash: PaymentHash, amt_msat: u64, cltv_expiry: u32) -> Result<PendingHTLCInfo, ReceiveError>
+       {
+               // final_incorrect_cltv_expiry
+               if hop_data.outgoing_cltv_value != cltv_expiry {
+                       return Err(ReceiveError {
+                               msg: "Upstream node set CLTV to the wrong value",
+                               err_code: 18,
+                               err_data: byte_utils::be32_to_array(cltv_expiry).to_vec()
+                       })
+               }
+               // final_expiry_too_soon
+               // We have to have some headroom to broadcast on chain if we have the preimage, so make sure
+               // we have at least HTLC_FAIL_BACK_BUFFER blocks to go.
+               // Also, ensure that, in the case of an unknown preimage for the received payment hash, our
+               // payment logic has enough time to fail the HTLC backward before our onchain logic triggers a
+               // channel closure (see HTLC_FAIL_BACK_BUFFER rationale).
+               if (hop_data.outgoing_cltv_value as u64) <= self.best_block.read().unwrap().height() as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1  {
+                       return Err(ReceiveError {
+                               err_code: 17,
+                               err_data: Vec::new(),
+                               msg: "The final CLTV expiry is too soon to handle",
+                       });
+               }
+               if hop_data.amt_to_forward > amt_msat {
+                       return Err(ReceiveError {
+                               err_code: 19,
+                               err_data: byte_utils::be64_to_array(amt_msat).to_vec(),
+                               msg: "Upstream node sent less than we were supposed to receive in payment",
+                       });
+               }
+
+               let routing = match hop_data.format {
+                       msgs::OnionHopDataFormat::Legacy { .. } => {
+                               return Err(ReceiveError {
+                                       err_code: 0x4000|0x2000|3,
+                                       err_data: Vec::new(),
+                                       msg: "We require payment_secrets",
+                               });
+                       },
+                       msgs::OnionHopDataFormat::NonFinalNode { .. } => {
+                               return Err(ReceiveError {
+                                       err_code: 0x4000|22,
+                                       err_data: Vec::new(),
+                                       msg: "Got non final data with an HMAC of 0",
+                               });
+                       },
+                       msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage } => {
+                               if payment_data.is_some() && keysend_preimage.is_some() {
+                                       return Err(ReceiveError {
+                                               err_code: 0x4000|22,
+                                               err_data: Vec::new(),
+                                               msg: "We don't support MPP keysend payments",
+                                       });
+                               } else if let Some(data) = payment_data {
+                                       PendingHTLCRouting::Receive {
+                                               payment_data: data,
+                                               incoming_cltv_expiry: hop_data.outgoing_cltv_value,
+                                       }
+                               } else if let Some(payment_preimage) = keysend_preimage {
+                                       // We need to check that the sender knows the keysend preimage before processing this
+                                       // payment further. Otherwise, an intermediary routing hop forwarding non-keysend-HTLC X
+                                       // could discover the final destination of X, by probing the adjacent nodes on the route
+                                       // with a keysend payment of identical payment hash to X and observing the processing
+                                       // time discrepancies due to a hash collision with X.
+                                       let hashed_preimage = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
+                                       if hashed_preimage != payment_hash {
+                                               return Err(ReceiveError {
+                                                       err_code: 0x4000|22,
+                                                       err_data: Vec::new(),
+                                                       msg: "Payment preimage didn't match payment hash",
+                                               });
+                                       }
+
+                                       PendingHTLCRouting::ReceiveKeysend {
+                                               payment_preimage,
+                                               incoming_cltv_expiry: hop_data.outgoing_cltv_value,
+                                       }
+                               } else {
+                                       return Err(ReceiveError {
+                                               err_code: 0x4000|0x2000|3,
+                                               err_data: Vec::new(),
+                                               msg: "We require payment_secrets",
+                                       });
+                               }
+                       },
+               };
+               Ok(PendingHTLCInfo {
+                       routing,
+                       payment_hash,
+                       incoming_shared_secret: shared_secret,
+                       amt_to_forward: amt_msat,
+                       outgoing_cltv_value: hop_data.outgoing_cltv_value,
+               })
+       }
+
        fn decode_update_add_htlc_onion(&self, msg: &msgs::UpdateAddHTLC) -> (PendingHTLCStatus, MutexGuard<ChannelHolder<Signer>>) {
                macro_rules! return_malformed_err {
                        ($msg: expr, $err_code: expr) => {
@@ -2088,7 +2191,6 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                        arr.copy_from_slice(&SharedSecret::new(&msg.onion_routing_packet.public_key.unwrap(), &self.our_network_key)[..]);
                        arr
                };
-               let (rho, mu) = onion_utils::gen_rho_mu_from_shared_secret(&shared_secret);
 
                if msg.onion_routing_packet.version != 0 {
                        //TODO: Spec doesn't indicate if we should only hash hop_data here (and in other
@@ -2100,13 +2202,6 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                        return_malformed_err!("Unknown onion packet version", 0x8000 | 0x4000 | 4);
                }
 
-               let mut hmac = HmacEngine::<Sha256>::new(&mu);
-               hmac.input(&msg.onion_routing_packet.hop_data);
-               hmac.input(&msg.payment_hash.0[..]);
-               if !fixed_time_eq(&Hmac::from_engine(hmac).into_inner(), &msg.onion_routing_packet.hmac) {
-                       return_malformed_err!("HMAC Check failed", 0x8000 | 0x4000 | 5);
-               }
-
                let mut channel_state = None;
                macro_rules! return_err {
                        ($msg: expr, $err_code: expr, $data: expr) => {
@@ -2124,164 +2219,70 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                        }
                }
 
-               let mut chacha = ChaCha20::new(&rho, &[0u8; 8]);
-               let mut chacha_stream = ChaChaReader { chacha: &mut chacha, read: Cursor::new(&msg.onion_routing_packet.hop_data[..]) };
-               let (next_hop_data, next_hop_hmac): (msgs::OnionHopData, _) = {
-                       match <msgs::OnionHopData as Readable>::read(&mut chacha_stream) {
-                               Err(err) => {
-                                       let error_code = match err {
-                                               msgs::DecodeError::UnknownVersion => 0x4000 | 1, // unknown realm byte
-                                               msgs::DecodeError::UnknownRequiredFeature|
-                                               msgs::DecodeError::InvalidValue|
-                                               msgs::DecodeError::ShortRead => 0x4000 | 22, // invalid_onion_payload
-                                               _ => 0x2000 | 2, // Should never happen
-                                       };
-                                       return_err!("Unable to decode our hop data", error_code, &[0;0]);
-                               },
-                               Ok(msg) => {
-                                       let mut hmac = [0; 32];
-                                       if let Err(_) = chacha_stream.read_exact(&mut hmac[..]) {
-                                               return_err!("Unable to decode hop data", 0x4000 | 22, &[0;0]);
-                                       }
-                                       (msg, hmac)
-                               },
-                       }
+               let next_hop = match onion_utils::decode_next_hop(shared_secret, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac, msg.payment_hash) {
+                       Ok(res) => res,
+                       Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
+                               return_malformed_err!(err_msg, err_code);
+                       },
+                       Err(onion_utils::OnionDecodeErr::Relay { err_msg, err_code }) => {
+                               return_err!(err_msg, err_code, &[0; 0]);
+                       },
                };
 
-               let pending_forward_info = if next_hop_hmac == [0; 32] {
-                       #[cfg(test)]
-                       {
-                               // In tests, make sure that the initial onion pcket data is, at least, non-0.
-                               // We could do some fancy randomness test here, but, ehh, whatever.
-                               // This checks for the issue where you can calculate the path length given the
-                               // onion data as all the path entries that the originator sent will be here
-                               // as-is (and were originally 0s).
-                               // Of course reverse path calculation is still pretty easy given naive routing
-                               // algorithms, but this fixes the most-obvious case.
-                               let mut next_bytes = [0; 32];
-                               chacha_stream.read_exact(&mut next_bytes).unwrap();
-                               assert_ne!(next_bytes[..], [0; 32][..]);
-                               chacha_stream.read_exact(&mut next_bytes).unwrap();
-                               assert_ne!(next_bytes[..], [0; 32][..]);
-                       }
+               let pending_forward_info = match next_hop {
+                       onion_utils::Hop::Receive(next_hop_data) => {
+                               // OUR PAYMENT!
+                               match self.construct_recv_pending_htlc_info(next_hop_data, shared_secret, msg.payment_hash, msg.amount_msat, msg.cltv_expiry) {
+                                       Ok(info) => {
+                                               // Note that we could obviously respond immediately with an update_fulfill_htlc
+                                               // message, however that would leak that we are the recipient of this payment, so
+                                               // instead we stay symmetric with the forwarding case, only responding (after a
+                                               // delay) once they've send us a commitment_signed!
+                                               PendingHTLCStatus::Forward(info)
+                                       },
+                                       Err(ReceiveError { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
+                               }
+                       },
+                       onion_utils::Hop::Forward { next_hop_data, next_hop_hmac, new_packet_bytes } => {
+                               let mut new_pubkey = msg.onion_routing_packet.public_key.unwrap();
+
+                               let blinding_factor = {
+                                       let mut sha = Sha256::engine();
+                                       sha.input(&new_pubkey.serialize()[..]);
+                                       sha.input(&shared_secret);
+                                       Sha256::from_engine(sha).into_inner()
+                               };
 
-                       // OUR PAYMENT!
-                       // final_expiry_too_soon
-                       // We have to have some headroom to broadcast on chain if we have the preimage, so make sure
-                       // we have at least HTLC_FAIL_BACK_BUFFER blocks to go.
-                       // Also, ensure that, in the case of an unknown preimage for the received payment hash, our
-                       // payment logic has enough time to fail the HTLC backward before our onchain logic triggers a
-                       // channel closure (see HTLC_FAIL_BACK_BUFFER rationale).
-                       if (msg.cltv_expiry as u64) <= self.best_block.read().unwrap().height() as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 {
-                               return_err!("The final CLTV expiry is too soon to handle", 17, &[0;0]);
-                       }
-                       // final_incorrect_htlc_amount
-                       if next_hop_data.amt_to_forward > msg.amount_msat {
-                               return_err!("Upstream node sent less than we were supposed to receive in payment", 19, &byte_utils::be64_to_array(msg.amount_msat));
-                       }
-                       // final_incorrect_cltv_expiry
-                       if next_hop_data.outgoing_cltv_value != msg.cltv_expiry {
-                               return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry));
-                       }
+                               let public_key = if let Err(e) = new_pubkey.mul_assign(&self.secp_ctx, &blinding_factor[..]) {
+                                       Err(e)
+                               } else { Ok(new_pubkey) };
 
-                       let routing = match next_hop_data.format {
-                               msgs::OnionHopDataFormat::Legacy { .. } => return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]),
-                               msgs::OnionHopDataFormat::NonFinalNode { .. } => return_err!("Got non final data with an HMAC of 0", 0x4000 | 22, &[0;0]),
-                               msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage } => {
-                                       if payment_data.is_some() && keysend_preimage.is_some() {
-                                               return_err!("We don't support MPP keysend payments", 0x4000|22, &[0;0]);
-                                       } else if let Some(data) = payment_data {
-                                               PendingHTLCRouting::Receive {
-                                                       payment_data: data,
-                                                       incoming_cltv_expiry: msg.cltv_expiry,
-                                               }
-                                       } else if let Some(payment_preimage) = keysend_preimage {
-                                               // We need to check that the sender knows the keysend preimage before processing this
-                                               // payment further. Otherwise, an intermediary routing hop forwarding non-keysend-HTLC X
-                                               // could discover the final destination of X, by probing the adjacent nodes on the route
-                                               // with a keysend payment of identical payment hash to X and observing the processing
-                                               // time discrepancies due to a hash collision with X.
-                                               let hashed_preimage = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
-                                               if hashed_preimage != msg.payment_hash {
-                                                       return_err!("Payment preimage didn't match payment hash", 0x4000|22, &[0;0]);
-                                               }
+                               let outgoing_packet = msgs::OnionPacket {
+                                       version: 0,
+                                       public_key,
+                                       hop_data: new_packet_bytes,
+                                       hmac: next_hop_hmac.clone(),
+                               };
 
-                                               PendingHTLCRouting::ReceiveKeysend {
-                                                       payment_preimage,
-                                                       incoming_cltv_expiry: msg.cltv_expiry,
-                                               }
-                                       } else {
-                                               return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]);
-                                       }
-                               },
-                       };
+                               let short_channel_id = match next_hop_data.format {
+                                       msgs::OnionHopDataFormat::Legacy { short_channel_id } => short_channel_id,
+                                       msgs::OnionHopDataFormat::NonFinalNode { short_channel_id } => short_channel_id,
+                                       msgs::OnionHopDataFormat::FinalNode { .. } => {
+                                               return_err!("Final Node OnionHopData provided for us as an intermediary node", 0x4000 | 22, &[0;0]);
+                                       },
+                               };
 
-                       // Note that we could obviously respond immediately with an update_fulfill_htlc
-                       // message, however that would leak that we are the recipient of this payment, so
-                       // instead we stay symmetric with the forwarding case, only responding (after a
-                       // delay) once they've send us a commitment_signed!
-
-                       PendingHTLCStatus::Forward(PendingHTLCInfo {
-                               routing,
-                               payment_hash: msg.payment_hash.clone(),
-                               incoming_shared_secret: shared_secret,
-                               amt_to_forward: next_hop_data.amt_to_forward,
-                               outgoing_cltv_value: next_hop_data.outgoing_cltv_value,
-                       })
-               } else {
-                       let mut new_packet_data = [0; 20*65];
-                       let read_pos = chacha_stream.read(&mut new_packet_data).unwrap();
-                       #[cfg(debug_assertions)]
-                       {
-                               // Check two things:
-                               // a) that the behavior of our stream here will return Ok(0) even if the TLV
-                               //    read above emptied out our buffer and the unwrap() wont needlessly panic
-                               // b) that we didn't somehow magically end up with extra data.
-                               let mut t = [0; 1];
-                               debug_assert!(chacha_stream.read(&mut t).unwrap() == 0);
+                               PendingHTLCStatus::Forward(PendingHTLCInfo {
+                                       routing: PendingHTLCRouting::Forward {
+                                               onion_packet: outgoing_packet,
+                                               short_channel_id,
+                                       },
+                                       payment_hash: msg.payment_hash.clone(),
+                                       incoming_shared_secret: shared_secret,
+                                       amt_to_forward: next_hop_data.amt_to_forward,
+                                       outgoing_cltv_value: next_hop_data.outgoing_cltv_value,
+                               })
                        }
-                       // Once we've emptied the set of bytes our peer gave us, encrypt 0 bytes until we
-                       // fill the onion hop data we'll forward to our next-hop peer.
-                       chacha_stream.chacha.process_in_place(&mut new_packet_data[read_pos..]);
-
-                       let mut new_pubkey = msg.onion_routing_packet.public_key.unwrap();
-
-                       let blinding_factor = {
-                               let mut sha = Sha256::engine();
-                               sha.input(&new_pubkey.serialize()[..]);
-                               sha.input(&shared_secret);
-                               Sha256::from_engine(sha).into_inner()
-                       };
-
-                       let public_key = if let Err(e) = new_pubkey.mul_assign(&self.secp_ctx, &blinding_factor[..]) {
-                               Err(e)
-                       } else { Ok(new_pubkey) };
-
-                       let outgoing_packet = msgs::OnionPacket {
-                               version: 0,
-                               public_key,
-                               hop_data: new_packet_data,
-                               hmac: next_hop_hmac.clone(),
-                       };
-
-                       let short_channel_id = match next_hop_data.format {
-                               msgs::OnionHopDataFormat::Legacy { short_channel_id } => short_channel_id,
-                               msgs::OnionHopDataFormat::NonFinalNode { short_channel_id } => short_channel_id,
-                               msgs::OnionHopDataFormat::FinalNode { .. } => {
-                                       return_err!("Final Node OnionHopData provided for us as an intermediary node", 0x4000 | 22, &[0;0]);
-                               },
-                       };
-
-                       PendingHTLCStatus::Forward(PendingHTLCInfo {
-                               routing: PendingHTLCRouting::Forward {
-                                       onion_packet: outgoing_packet,
-                                       short_channel_id,
-                               },
-                               payment_hash: msg.payment_hash.clone(),
-                               incoming_shared_secret: shared_secret,
-                               amt_to_forward: next_hop_data.amt_to_forward,
-                               outgoing_cltv_value: next_hop_data.outgoing_cltv_value,
-                       })
                };
 
                channel_state = Some(self.channel_state.lock().unwrap());
@@ -2292,48 +2293,59 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                        if let &PendingHTLCRouting::Forward { ref short_channel_id, .. } = routing {
                                let id_option = channel_state.as_ref().unwrap().short_to_id.get(&short_channel_id).cloned();
                                if let Some((err, code, chan_update)) = loop {
-                                       let forwarding_id = match id_option {
+                                       let forwarding_id_opt = match id_option {
                                                None => { // unknown_next_peer
-                                                       break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
+                                                       // Note that this is likely a timing oracle for detecting whether an scid is a
+                                                       // phantom.
+                                                       if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id) {
+                                                               None
+                                                       } else {
+                                                               break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
+                                                       }
                                                },
-                                               Some(id) => id.clone(),
+                                               Some(id) => Some(id.clone()),
                                        };
+                                       let (chan_update_opt, forwardee_cltv_expiry_delta) = if let Some(forwarding_id) = forwarding_id_opt {
+                                               let chan = channel_state.as_mut().unwrap().by_id.get_mut(&forwarding_id).unwrap();
+                                               // Leave channel updates as None for private channels.
+                                               let chan_update_opt = if chan.should_announce() {
+                                                       Some(self.get_channel_update_for_unicast(chan).unwrap()) } else { None };
+                                               if !chan.should_announce() && !self.default_configuration.accept_forwards_to_priv_channels {
+                                                       // Note that the behavior here should be identical to the above block - we
+                                                       // should NOT reveal the existence or non-existence of a private channel if
+                                                       // we don't allow forwards outbound over them.
+                                                       break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
+                                               }
 
-                                       let chan = channel_state.as_mut().unwrap().by_id.get_mut(&forwarding_id).unwrap();
-
-                                       if !chan.should_announce() && !self.default_configuration.accept_forwards_to_priv_channels {
-                                               // Note that the behavior here should be identical to the above block - we
-                                               // should NOT reveal the existence or non-existence of a private channel if
-                                               // we don't allow forwards outbound over them.
-                                               break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
-                                       }
+                                               // Note that we could technically not return an error yet here and just hope
+                                               // that the connection is reestablished or monitor updated by the time we get
+                                               // around to doing the actual forward, but better to fail early if we can and
+                                               // hopefully an attacker trying to path-trace payments cannot make this occur
+                                               // on a small/per-node/per-channel scale.
+                                               if !chan.is_live() { // channel_disabled
+                                                       break Some(("Forwarding channel is not in a ready state.", 0x1000 | 20, chan_update_opt));
+                                               }
+                                               if *amt_to_forward < chan.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
+                                                       break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, chan_update_opt));
+                                               }
+                                               let fee = amt_to_forward.checked_mul(chan.get_fee_proportional_millionths() as u64)
+                                                       .and_then(|prop_fee| { (prop_fee / 1000000)
+                                                       .checked_add(chan.get_outbound_forwarding_fee_base_msat() as u64) });
+                                               if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward { // fee_insufficient
+                                                       break Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, chan_update_opt));
+                                               }
+                                               (chan_update_opt, chan.get_cltv_expiry_delta())
+                                       } else { (None, MIN_CLTV_EXPIRY_DELTA) };
 
-                                       // Note that we could technically not return an error yet here and just hope
-                                       // that the connection is reestablished or monitor updated by the time we get
-                                       // around to doing the actual forward, but better to fail early if we can and
-                                       // hopefully an attacker trying to path-trace payments cannot make this occur
-                                       // on a small/per-node/per-channel scale.
-                                       if !chan.is_live() { // channel_disabled
-                                               break Some(("Forwarding channel is not in a ready state.", 0x1000 | 20, Some(self.get_channel_update_for_unicast(chan).unwrap())));
-                                       }
-                                       if *amt_to_forward < chan.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
-                                               break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, Some(self.get_channel_update_for_unicast(chan).unwrap())));
-                                       }
-                                       let fee = amt_to_forward.checked_mul(chan.get_fee_proportional_millionths() as u64)
-                                               .and_then(|prop_fee| { (prop_fee / 1000000)
-                                               .checked_add(chan.get_outbound_forwarding_fee_base_msat() as u64) });
-                                       if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward { // fee_insufficient
-                                               break Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, Some(self.get_channel_update_for_unicast(chan).unwrap())));
-                                       }
-                                       if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + chan.get_cltv_expiry_delta() as u64 { // incorrect_cltv_expiry
-                                               break Some(("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, Some(self.get_channel_update_for_unicast(chan).unwrap())));
+                                       if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + forwardee_cltv_expiry_delta as u64 { // incorrect_cltv_expiry
+                                               break Some(("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, chan_update_opt));
                                        }
                                        let cur_height = self.best_block.read().unwrap().height() + 1;
                                        // Theoretically, channel counterparty shouldn't send us a HTLC expiring now,
                                        // but we want to be robust wrt to counterparty packet sanitization (see
                                        // HTLC_FAIL_BACK_BUFFER rationale).
                                        if msg.cltv_expiry <= cur_height + HTLC_FAIL_BACK_BUFFER as u32 { // expiry_too_soon
-                                               break Some(("CLTV expiry is too close", 0x1000 | 14, Some(self.get_channel_update_for_unicast(chan).unwrap())));
+                                               break Some(("CLTV expiry is too close", 0x1000 | 14, chan_update_opt));
                                        }
                                        if msg.cltv_expiry > cur_height + CLTV_FAR_FAR_AWAY as u32 { // expiry_too_far
                                                break Some(("CLTV expiry is too far in the future", 21, None));
@@ -2347,7 +2359,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                        // but there is no need to do that, and since we're a bit conservative with our
                                        // risk threshold it just results in failing to forward payments.
                                        if (*outgoing_cltv_value) as u64 <= (cur_height + LATENCY_GRACE_PERIOD_BLOCKS) as u64 {
-                                               break Some(("Outgoing CLTV value is too soon", 0x1000 | 14, Some(self.get_channel_update_for_unicast(chan).unwrap())));
+                                               break Some(("Outgoing CLTV value is too soon", 0x1000 | 14, chan_update_opt));
                                        }
 
                                        break None;
@@ -2983,6 +2995,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
 
                let mut new_events = Vec::new();
                let mut failed_forwards = Vec::new();
+               let mut phantom_receives: Vec<(u64, OutPoint, Vec<(PendingHTLCInfo, u64)>)> = Vec::new();
                let mut handle_errors = Vec::new();
                {
                        let mut channel_state_lock = self.channel_state.lock().unwrap();
@@ -2993,26 +3006,69 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                        let forward_chan_id = match channel_state.short_to_id.get(&short_chan_id) {
                                                Some(chan_id) => chan_id.clone(),
                                                None => {
-                                                       failed_forwards.reserve(pending_forwards.len());
                                                        for forward_info in pending_forwards.drain(..) {
                                                                match forward_info {
-                                                                       HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info,
-                                                                                                  prev_funding_outpoint } => {
-                                                                               let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
-                                                                                       short_channel_id: prev_short_channel_id,
-                                                                                       outpoint: prev_funding_outpoint,
-                                                                                       htlc_id: prev_htlc_id,
-                                                                                       incoming_packet_shared_secret: forward_info.incoming_shared_secret,
-                                                                               });
-                                                                               failed_forwards.push((htlc_source, forward_info.payment_hash,
-                                                                                       HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() }
-                                                                               ));
-                                                                       },
+                                                                       HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
+                                                                               routing, incoming_shared_secret, payment_hash, amt_to_forward, outgoing_cltv_value },
+                                                                               prev_funding_outpoint } => {
+                                                                                       macro_rules! fail_forward {
+                                                                                               ($msg: expr, $err_code: expr, $err_data: expr) => {
+                                                                                                       {
+                                                                                                               log_info!(self.logger, "Failed to accept/forward incoming HTLC: {}", $msg);
+                                                                                                               let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
+                                                                                                                       short_channel_id: short_chan_id,
+                                                                                                                       outpoint: prev_funding_outpoint,
+                                                                                                                       htlc_id: prev_htlc_id,
+                                                                                                                       incoming_packet_shared_secret: incoming_shared_secret,
+                                                                                                               });
+                                                                                                               failed_forwards.push((htlc_source, payment_hash,
+                                                                                                                               HTLCFailReason::Reason { failure_code: $err_code, data: $err_data }
+                                                                                                               ));
+                                                                                                               continue;
+                                                                                                       }
+                                                                                               }
+                                                                                       }
+                                                                                       if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
+                                                                                               let phantom_secret_res = self.keys_manager.get_node_secret(Recipient::PhantomNode);
+                                                                                               if phantom_secret_res.is_ok() && fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, short_chan_id) {
+                                                                                                       let shared_secret = {
+                                                                                                               let mut arr = [0; 32];
+                                                                                                               arr.copy_from_slice(&SharedSecret::new(&onion_packet.public_key.unwrap(), &phantom_secret_res.unwrap())[..]);
+                                                                                                               arr
+                                                                                                       };
+                                                                                                       let next_hop = match onion_utils::decode_next_hop(shared_secret, &onion_packet.hop_data, onion_packet.hmac, payment_hash) {
+                                                                                                               Ok(res) => res,
+                                                                                                               Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
+                                                                                                                       fail_forward!(err_msg, err_code, Vec::new());
+                                                                                                               },
+                                                                                                               Err(onion_utils::OnionDecodeErr::Relay { err_msg, err_code }) => {
+                                                                                                                       fail_forward!(err_msg, err_code, Vec::new());
+                                                                                                               },
+                                                                                                       };
+                                                                                                       match next_hop {
+                                                                                                               onion_utils::Hop::Receive(hop_data) => {
+                                                                                                                       match self.construct_recv_pending_htlc_info(hop_data, shared_secret, payment_hash, amt_to_forward, outgoing_cltv_value) {
+                                                                                                                               Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, vec![(info, prev_htlc_id)])),
+                                                                                                                               Err(ReceiveError { err_code, err_data, msg }) => fail_forward!(msg, err_code, err_data)
+                                                                                                                       }
+                                                                                                               },
+                                                                                                               _ => panic!(),
+                                                                                                       }
+                                                                                               } else {
+                                                                                                       fail_forward!(format!("Unknown short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new());
+                                                                                               }
+                                                                                       } else {
+                                                                                               fail_forward!(format!("Unknown short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new());
+                                                                                       }
+                                                                               },
                                                                        HTLCForwardInfo::FailHTLC { .. } => {
                                                                                // Channel went away before we could fail it. This implies
                                                                                // the channel is now on chain and our counterparty is
                                                                                // trying to broadcast the HTLC-Timeout, but that's their
                                                                                // problem, not ours.
+                                                                               //
+                                                                               // `fail_htlc_backwards_internal` is never called for
+                                                                               // phantom payments, so this is unreachable for them.
                                                                        }
                                                                }
                                                        }
@@ -3318,6 +3374,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                for (htlc_source, payment_hash, failure_reason) in failed_forwards.drain(..) {
                        self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), htlc_source, &payment_hash, failure_reason);
                }
+               self.forward_htlcs(&mut phantom_receives);
 
                for (counterparty_node_id, err) in handle_errors.drain(..) {
                        let _ = handle_error!(self, err, counterparty_node_id);
@@ -5150,6 +5207,34 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
        }
 
+       /// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids
+       /// are used when constructing the phantom invoice's route hints.
+       ///
+       /// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+       pub fn get_phantom_scid(&self) -> u64 {
+               let mut channel_state = self.channel_state.lock().unwrap();
+               let best_block = self.best_block.read().unwrap();
+               loop {
+                       let scid_candidate = fake_scid::get_phantom_scid(&self.fake_scid_rand_bytes, best_block.height(), &self.genesis_hash, &self.keys_manager);
+                       // Ensure the generated scid doesn't conflict with a real channel.
+                       match channel_state.short_to_id.entry(scid_candidate) {
+                               hash_map::Entry::Occupied(_) => continue,
+                               hash_map::Entry::Vacant(_) => return scid_candidate
+                       }
+               }
+       }
+
+       /// Gets route hints for use in receiving [phantom node payments].
+       ///
+       /// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
+       pub fn get_phantom_route_hints(&self) -> PhantomRouteHints {
+               PhantomRouteHints {
+                       channels: self.list_usable_channels(),
+                       phantom_scid: self.get_phantom_scid(),
+                       real_node_pubkey: self.get_our_node_id(),
+               }
+       }
+
        #[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
        pub fn get_and_clear_pending_events(&self) -> Vec<events::Event> {
                let events = core::cell::RefCell::new(Vec::new());
@@ -5848,6 +5933,44 @@ impl PersistenceNotifier {
 const SERIALIZATION_VERSION: u8 = 1;
 const MIN_SERIALIZATION_VERSION: u8 = 1;
 
+impl_writeable_tlv_based!(CounterpartyForwardingInfo, {
+       (2, fee_base_msat, required),
+       (4, fee_proportional_millionths, required),
+       (6, cltv_expiry_delta, required),
+});
+
+impl_writeable_tlv_based!(ChannelCounterparty, {
+       (2, node_id, required),
+       (4, features, required),
+       (6, unspendable_punishment_reserve, required),
+       (8, forwarding_info, option),
+});
+
+impl_writeable_tlv_based!(ChannelDetails, {
+       (2, channel_id, required),
+       (4, counterparty, required),
+       (6, funding_txo, option),
+       (8, short_channel_id, option),
+       (10, channel_value_satoshis, required),
+       (12, unspendable_punishment_reserve, option),
+       (14, user_channel_id, required),
+       (16, balance_msat, required),
+       (18, outbound_capacity_msat, required),
+       (20, inbound_capacity_msat, required),
+       (22, confirmations_required, option),
+       (24, force_close_spend_delay, option),
+       (26, is_outbound, required),
+       (28, is_funding_locked, required),
+       (30, is_usable, required),
+       (32, is_public, required),
+});
+
+impl_writeable_tlv_based!(PhantomRouteHints, {
+       (2, channels, vec_type),
+       (4, phantom_scid, required),
+       (6, real_node_pubkey, required),
+});
+
 impl_writeable_tlv_based_enum!(PendingHTLCRouting,
        (0, Forward) => {
                (0, onion_packet, required),
@@ -6249,7 +6372,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
                write_tlv_fields!(writer, {
                        (1, pending_outbound_payments_no_retry, required),
                        (3, pending_outbound_payments, required),
-                       (5, self.our_network_pubkey, required)
+                       (5, self.our_network_pubkey, required),
+                       (7, self.fake_scid_rand_bytes, required),
                });
 
                Ok(())
@@ -6545,11 +6669,16 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                let mut pending_outbound_payments_no_retry: Option<HashMap<PaymentId, HashSet<[u8; 32]>>> = None;
                let mut pending_outbound_payments = None;
                let mut received_network_pubkey: Option<PublicKey> = None;
+               let mut fake_scid_rand_bytes: Option<[u8; 32]> = None;
                read_tlv_fields!(reader, {
                        (1, pending_outbound_payments_no_retry, option),
                        (3, pending_outbound_payments, option),
-                       (5, received_network_pubkey, option)
+                       (5, received_network_pubkey, option),
+                       (7, fake_scid_rand_bytes, option),
                });
+               if fake_scid_rand_bytes.is_none() {
+                       fake_scid_rand_bytes = Some(args.keys_manager.get_secure_random_bytes());
+               }
 
                if pending_outbound_payments.is_none() && pending_outbound_payments_no_retry.is_none() {
                        pending_outbound_payments = Some(pending_outbound_payments_compat);
@@ -6613,7 +6742,11 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                        pending_events_read.append(&mut channel_closures);
                }
 
-               let our_network_pubkey = PublicKey::from_secret_key(&secp_ctx, &args.keys_manager.get_node_secret());
+               let our_network_key = match args.keys_manager.get_node_secret(Recipient::Node) {
+                       Ok(key) => key,
+                       Err(()) => return Err(DecodeError::InvalidValue)
+               };
+               let our_network_pubkey = PublicKey::from_secret_key(&secp_ctx, &our_network_key);
                if let Some(network_pubkey) = received_network_pubkey {
                        if network_pubkey != our_network_pubkey {
                                log_error!(args.logger, "Key that was generated does not match the existing key.");
@@ -6641,8 +6774,9 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                        inbound_payment_key: expanded_inbound_key,
                        pending_inbound_payments: Mutex::new(pending_inbound_payments),
                        pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
+                       fake_scid_rand_bytes: fake_scid_rand_bytes.unwrap(),
 
-                       our_network_key: args.keys_manager.get_node_secret(),
+                       our_network_key,
                        our_network_pubkey,
                        secp_ctx,
 
@@ -7172,7 +7306,7 @@ mod tests {
        }
 }
 
-#[cfg(all(any(test, feature = "_test_utils"), feature = "unstable"))]
+#[cfg(all(any(test, feature = "_test_utils"), feature = "_bench_unstable"))]
 pub mod bench {
        use chain::Listen;
        use chain::chainmonitor::{ChainMonitor, Persist};
index e8931c1edc00f9f0f3156e3515c512e594e963e2..1d9e5afc46eecb7c66abac96528aa763a4030bbb 100644 (file)
@@ -1102,7 +1102,7 @@ macro_rules! expect_pending_htlcs_forwardable_ignore {
                let events = $node.node.get_and_clear_pending_events();
                assert_eq!(events.len(), 1);
                match events[0] {
-                       Event::PendingHTLCsForwardable { .. } => { },
+                       $crate::util::events::Event::PendingHTLCsForwardable { .. } => { },
                        _ => panic!("Unexpected event"),
                };
        }}
@@ -1137,18 +1137,22 @@ macro_rules! expect_pending_htlcs_forwardable_from_events {
        }}
 }
 
-#[cfg(any(test, feature = "unstable"))]
+#[macro_export]
+#[cfg(any(test, feature = "_bench_unstable", feature = "_test_utils"))]
 macro_rules! expect_payment_received {
        ($node: expr, $expected_payment_hash: expr, $expected_payment_secret: expr, $expected_recv_value: expr) => {
+               expect_payment_received!($node, $expected_payment_hash, $expected_payment_secret, $expected_recv_value, None)
+       };
+       ($node: expr, $expected_payment_hash: expr, $expected_payment_secret: expr, $expected_recv_value: expr, $expected_payment_preimage: expr) => {
                let events = $node.node.get_and_clear_pending_events();
                assert_eq!(events.len(), 1);
                match events[0] {
-                       Event::PaymentReceived { ref payment_hash, ref purpose, amt } => {
+                       $crate::util::events::Event::PaymentReceived { ref payment_hash, ref purpose, amt } => {
                                assert_eq!($expected_payment_hash, *payment_hash);
                                assert_eq!($expected_recv_value, amt);
                                match purpose {
-                                       PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
-                                               assert!(payment_preimage.is_none());
+                                       $crate::util::events::PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
+                                               assert_eq!(&$expected_payment_preimage, payment_preimage);
                                                assert_eq!($expected_payment_secret, *payment_secret);
                                        },
                                        _ => {},
@@ -1560,7 +1564,7 @@ pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_rou
                .with_features(InvoiceFeatures::known());
        let scorer = test_utils::TestScorer::with_penalty(0);
        let route = get_route(
-               &origin_node.node.get_our_node_id(), &payment_params, origin_node.network_graph, 
+               &origin_node.node.get_our_node_id(), &payment_params, origin_node.network_graph,
                None, recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer).unwrap();
        assert_eq!(route.paths.len(), 1);
        assert_eq!(route.paths[0].len(), expected_route.len());
index 9007f3f2ea7d59aaa820c0999e5c069be3718935..203e2426fec9f5a5387526870b8b43c8e3598336 100644 (file)
@@ -985,7 +985,7 @@ impl fmt::Display for DecodeError {
                        DecodeError::InvalidValue => f.write_str("Nonsense bytes didn't map to the type they were interpreted as"),
                        DecodeError::ShortRead => f.write_str("Packet extended beyond the provided bytes"),
                        DecodeError::BadLengthDescriptor => f.write_str("A length descriptor in the packet didn't describe the later data correctly"),
-                       DecodeError::Io(ref e) => e.fmt(f),
+                       DecodeError::Io(ref e) => fmt::Debug::fmt(e, f),
                        DecodeError::UnsupportedCompression => f.write_str("We don't support receiving messages with zlib-compressed fields"),
                }
        }
index ec668045eb00614ba9ceef4df949d60572e962c4..0dd6087f82018d9faf56d53672325b76284caa00 100644 (file)
@@ -12,7 +12,7 @@ use ln::channelmanager::HTLCSource;
 use ln::msgs;
 use routing::network_graph::NetworkUpdate;
 use routing::router::RouteHop;
-use util::chacha20::ChaCha20;
+use util::chacha20::{ChaCha20, ChaChaReader};
 use util::errors::{self, APIError};
 use util::ser::{Readable, Writeable, LengthCalculatingWriter};
 use util::logger::Logger;
@@ -28,7 +28,7 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
 use bitcoin::secp256k1;
 
 use prelude::*;
-use io::Cursor;
+use io::{Cursor, Read};
 use core::convert::TryInto;
 use core::ops::Deref;
 
@@ -506,6 +506,114 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
        } else { unreachable!(); }
 }
 
+/// Data decrypted from the onion payload.
+pub(crate) enum Hop {
+       /// This onion payload was for us, not for forwarding to a next-hop. Contains information for
+       /// verifying the incoming payment.
+       Receive(msgs::OnionHopData),
+       /// This onion payload needs to be forwarded to a next-hop.
+       Forward {
+               /// Onion payload data used in forwarding the payment.
+               next_hop_data: msgs::OnionHopData,
+               /// HMAC of the next hop's onion packet.
+               next_hop_hmac: [u8; 32],
+               /// Bytes of the onion packet we're forwarding.
+               new_packet_bytes: [u8; 20*65],
+       },
+}
+
+/// Error returned when we fail to decode the onion packet.
+pub(crate) enum OnionDecodeErr {
+       /// The HMAC of the onion packet did not match the hop data.
+       Malformed {
+               err_msg: &'static str,
+               err_code: u16,
+       },
+       /// We failed to decode the onion payload.
+       Relay {
+               err_msg: &'static str,
+               err_code: u16,
+       },
+}
+
+pub(crate) fn decode_next_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash) -> Result<Hop, OnionDecodeErr> {
+       let (rho, mu) = gen_rho_mu_from_shared_secret(&shared_secret);
+       let mut hmac = HmacEngine::<Sha256>::new(&mu);
+       hmac.input(hop_data);
+       hmac.input(&payment_hash.0[..]);
+       if !fixed_time_eq(&Hmac::from_engine(hmac).into_inner(), &hmac_bytes) {
+               return Err(OnionDecodeErr::Malformed {
+                       err_msg: "HMAC Check failed",
+                       err_code: 0x8000 | 0x4000 | 5,
+               });
+       }
+
+       let mut chacha = ChaCha20::new(&rho, &[0u8; 8]);
+       let mut chacha_stream = ChaChaReader { chacha: &mut chacha, read: Cursor::new(&hop_data[..]) };
+       match <msgs::OnionHopData as Readable>::read(&mut chacha_stream) {
+               Err(err) => {
+                       let error_code = match err {
+                               msgs::DecodeError::UnknownVersion => 0x4000 | 1, // unknown realm byte
+                               msgs::DecodeError::UnknownRequiredFeature|
+                               msgs::DecodeError::InvalidValue|
+                               msgs::DecodeError::ShortRead => 0x4000 | 22, // invalid_onion_payload
+                               _ => 0x2000 | 2, // Should never happen
+                       };
+                       return Err(OnionDecodeErr::Relay {
+                               err_msg: "Unable to decode our hop data",
+                               err_code: error_code,
+                       });
+               },
+               Ok(msg) => {
+                       let mut hmac = [0; 32];
+                       if let Err(_) = chacha_stream.read_exact(&mut hmac[..]) {
+                               return Err(OnionDecodeErr::Relay {
+                                       err_msg: "Unable to decode our hop data",
+                                       err_code: 0x4000 | 22,
+                               });
+                       }
+                       if hmac == [0; 32] {
+                               #[cfg(test)]
+                               {
+                                       // In tests, make sure that the initial onion packet data is, at least, non-0.
+                                       // We could do some fancy randomness test here, but, ehh, whatever.
+                                       // This checks for the issue where you can calculate the path length given the
+                                       // onion data as all the path entries that the originator sent will be here
+                                       // as-is (and were originally 0s).
+                                       // Of course reverse path calculation is still pretty easy given naive routing
+                                       // algorithms, but this fixes the most-obvious case.
+                                       let mut next_bytes = [0; 32];
+                                       chacha_stream.read_exact(&mut next_bytes).unwrap();
+                                       assert_ne!(next_bytes[..], [0; 32][..]);
+                                       chacha_stream.read_exact(&mut next_bytes).unwrap();
+                                       assert_ne!(next_bytes[..], [0; 32][..]);
+                               }
+                               return Ok(Hop::Receive(msg));
+                       } else {
+                               let mut new_packet_bytes = [0; 20*65];
+                               let read_pos = chacha_stream.read(&mut new_packet_bytes).unwrap();
+                               #[cfg(debug_assertions)]
+                               {
+                                       // Check two things:
+                                       // a) that the behavior of our stream here will return Ok(0) even if the TLV
+                                       //    read above emptied out our buffer and the unwrap() wont needlessly panic
+                                       // b) that we didn't somehow magically end up with extra data.
+                                       let mut t = [0; 1];
+                                       debug_assert!(chacha_stream.read(&mut t).unwrap() == 0);
+                               }
+                               // Once we've emptied the set of bytes our peer gave us, encrypt 0 bytes until we
+                               // fill the onion hop data we'll forward to our next-hop peer.
+                               chacha_stream.chacha.process_in_place(&mut new_packet_bytes[read_pos..]);
+                               return Ok(Hop::Forward {
+                                       next_hop_data: msg,
+                                       next_hop_hmac: hmac,
+                                       new_packet_bytes,
+                               })
+                       }
+               },
+       }
+}
+
 #[cfg(test)]
 mod tests {
        use io;
index 7b42c68a578154aedcc0116cffac9c1a4ee5d799..fbd32526ea658470f810c487bcb88da608904e8b 100644 (file)
@@ -12,7 +12,7 @@ use prelude::*;
 use ln::msgs::LightningError;
 use ln::msgs;
 
-use bitcoin::hashes::{Hash, HashEngine, Hmac, HmacEngine};
+use bitcoin::hashes::{Hash, HashEngine};
 use bitcoin::hashes::sha256::Hash as Sha256;
 
 use bitcoin::secp256k1::Secp256k1;
@@ -21,6 +21,7 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
 use bitcoin::secp256k1;
 
 use util::chacha20poly1305rfc::ChaCha20Poly1305RFC;
+use util::crypto::hkdf_extract_expand_twice;
 use bitcoin::hashes::hex::ToHex;
 
 /// Maximum Lightning message data length according to
@@ -160,22 +161,9 @@ impl PeerChannelEncryptor {
                Ok(())
        }
 
-       fn hkdf_extract_expand(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
-               let mut hmac = HmacEngine::<Sha256>::new(salt);
-               hmac.input(ikm);
-               let prk = Hmac::from_engine(hmac).into_inner();
-               let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
-               hmac.input(&[1; 1]);
-               let t1 = Hmac::from_engine(hmac).into_inner();
-               let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
-               hmac.input(&t1);
-               hmac.input(&[2; 1]);
-               (t1, Hmac::from_engine(hmac).into_inner())
-       }
-
        #[inline]
        fn hkdf(state: &mut BidirectionalNoiseState, ss: SharedSecret) -> [u8; 32] {
-               let (t1, t2) = Self::hkdf_extract_expand(&state.ck, &ss[..]);
+               let (t1, t2) = hkdf_extract_expand_twice(&state.ck, &ss[..]);
                state.ck = t1;
                t2
        }
@@ -311,7 +299,7 @@ impl PeerChannelEncryptor {
                                                let temp_k = PeerChannelEncryptor::hkdf(bidirectional_state, ss);
 
                                                PeerChannelEncryptor::encrypt_with_ad(&mut res[50..], 0, &temp_k, &bidirectional_state.h, &[0; 0]);
-                                               final_hkdf = Self::hkdf_extract_expand(&bidirectional_state.ck, &[0; 0]);
+                                               final_hkdf = hkdf_extract_expand_twice(&bidirectional_state.ck, &[0; 0]);
                                                ck = bidirectional_state.ck.clone();
                                                res
                                        },
@@ -365,7 +353,7 @@ impl PeerChannelEncryptor {
                                                let temp_k = PeerChannelEncryptor::hkdf(bidirectional_state, ss);
 
                                                PeerChannelEncryptor::decrypt_with_ad(&mut [0; 0], 0, &temp_k, &bidirectional_state.h, &act_three[50..])?;
-                                               final_hkdf = Self::hkdf_extract_expand(&bidirectional_state.ck, &[0; 0]);
+                                               final_hkdf = hkdf_extract_expand_twice(&bidirectional_state.ck, &[0; 0]);
                                                ck = bidirectional_state.ck.clone();
                                        },
                                        _ => panic!("Wrong direction for act"),
@@ -399,7 +387,7 @@ impl PeerChannelEncryptor {
                match self.noise_state {
                        NoiseState::Finished { ref mut sk, ref mut sn, ref mut sck, rk: _, rn: _, rck: _ } => {
                                if *sn >= 1000 {
-                                       let (new_sck, new_sk) = Self::hkdf_extract_expand(sck, sk);
+                                       let (new_sck, new_sk) = hkdf_extract_expand_twice(sck, sk);
                                        *sck = new_sck;
                                        *sk = new_sk;
                                        *sn = 0;
@@ -425,7 +413,7 @@ impl PeerChannelEncryptor {
                match self.noise_state {
                        NoiseState::Finished { sk: _, sn: _, sck: _, ref mut rk, ref mut rn, ref mut rck } => {
                                if *rn >= 1000 {
-                                       let (new_rck, new_rk) = Self::hkdf_extract_expand(rck, rk);
+                                       let (new_rck, new_rk) = hkdf_extract_expand_twice(rck, rk);
                                        *rck = new_rck;
                                        *rk = new_rk;
                                        *rn = 0;
index ee310830afa93ee8e4bc55cccba603cf26897ac2..5a84f35c28dd4f1990ef0eda4611c53a139cb615 100644 (file)
@@ -2687,7 +2687,7 @@ mod tests {
        }
 }
 
-#[cfg(all(test, feature = "unstable"))]
+#[cfg(all(test, feature = "_bench_unstable"))]
 mod benches {
        use super::*;
 
index a21cf79a144dfecf47603da633f887992b068d5b..dc2f8fd1196a1848af7390af2bc7642c9265f1df 100644 (file)
@@ -455,7 +455,7 @@ struct PathBuildingHop<'a> {
        /// decrease as well. Thus, we have to explicitly track which nodes have been processed and
        /// avoid processing them again.
        was_processed: bool,
-       #[cfg(any(test, feature = "fuzztarget"))]
+       #[cfg(all(not(feature = "_bench_unstable"), any(test, feature = "fuzztarget")))]
        // In tests, we apply further sanity checks on cases where we skip nodes we already processed
        // to ensure it is specifically in cases where the fee has gone down because of a decrease in
        // value_contribution_msat, which requires tracking it here. See comments below where it is
@@ -896,14 +896,14 @@ where L::Target: Logger {
                                                                path_htlc_minimum_msat,
                                                                path_penalty_msat: u64::max_value(),
                                                                was_processed: false,
-                                                               #[cfg(any(test, feature = "fuzztarget"))]
+                                                               #[cfg(all(not(feature = "_bench_unstable"), any(test, feature = "fuzztarget")))]
                                                                value_contribution_msat,
                                                        }
                                                });
 
                                                #[allow(unused_mut)] // We only use the mut in cfg(test)
                                                let mut should_process = !old_entry.was_processed;
-                                               #[cfg(any(test, feature = "fuzztarget"))]
+                                               #[cfg(all(not(feature = "_bench_unstable"), any(test, feature = "fuzztarget")))]
                                                {
                                                        // In test/fuzzing builds, we do extra checks to make sure the skipping
                                                        // of already-seen nodes only happens in cases we expect (see below).
@@ -992,13 +992,13 @@ where L::Target: Logger {
                                                                old_entry.fee_msat = 0; // This value will be later filled with hop_use_fee_msat of the following channel
                                                                old_entry.path_htlc_minimum_msat = path_htlc_minimum_msat;
                                                                old_entry.path_penalty_msat = path_penalty_msat;
-                                                               #[cfg(any(test, feature = "fuzztarget"))]
+                                                               #[cfg(all(not(feature = "_bench_unstable"), any(test, feature = "fuzztarget")))]
                                                                {
                                                                        old_entry.value_contribution_msat = value_contribution_msat;
                                                                }
                                                                did_add_update_path_to_src_node = true;
                                                        } else if old_entry.was_processed && new_cost < old_cost {
-                                                               #[cfg(any(test, feature = "fuzztarget"))]
+                                                               #[cfg(all(not(feature = "_bench_unstable"), any(test, feature = "fuzztarget")))]
                                                                {
                                                                        // If we're skipping processing a node which was previously
                                                                        // processed even though we found another path to it with a
@@ -4976,7 +4976,7 @@ pub(crate) mod test_utils {
        }
 }
 
-#[cfg(all(test, feature = "unstable", not(feature = "no-std")))]
+#[cfg(all(test, feature = "_bench_unstable", not(feature = "no-std")))]
 mod benches {
        use super::*;
        use bitcoin::hashes::Hash;
diff --git a/lightning/src/util/crypto.rs b/lightning/src/util/crypto.rs
new file mode 100644 (file)
index 0000000..f8a3f84
--- /dev/null
@@ -0,0 +1,38 @@
+use bitcoin::hashes::{Hash, HashEngine};
+use bitcoin::hashes::hmac::{Hmac, HmacEngine};
+use bitcoin::hashes::sha256::Hash as Sha256;
+
+macro_rules! hkdf_extract_expand {
+       ($salt: expr, $ikm: expr) => {{
+               let mut hmac = HmacEngine::<Sha256>::new($salt);
+               hmac.input($ikm);
+               let prk = Hmac::from_engine(hmac).into_inner();
+               let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
+               hmac.input(&[1; 1]);
+               let t1 = Hmac::from_engine(hmac).into_inner();
+               let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
+               hmac.input(&t1);
+               hmac.input(&[2; 1]);
+               (t1, Hmac::from_engine(hmac).into_inner(), prk)
+       }};
+       ($salt: expr, $ikm: expr, 2) => {{
+               let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm);
+               (k1, k2)
+       }};
+       ($salt: expr, $ikm: expr, 3) => {{
+               let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm);
+
+               let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
+               hmac.input(&k2);
+               hmac.input(&[3; 1]);
+               (k1, k2, Hmac::from_engine(hmac).into_inner())
+       }}
+}
+
+pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
+       hkdf_extract_expand!(salt, ikm, 2)
+}
+
+pub fn hkdf_extract_expand_thrice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32], [u8; 32]) {
+       hkdf_extract_expand!(salt, ikm, 3)
+}
index 81b4bc927ce214f75d4b02f0dd27673abc4d2bd6..6e04f85682dd7fb59da7b50c7a6322660ebf0e96 100644 (file)
@@ -38,6 +38,9 @@ pub(crate) mod scid_utils;
 #[macro_use]
 pub(crate) mod macro_logger;
 
+/// Cryptography utilities.
+pub(crate) mod crypto;
+
 // These have to come after macro_logger to build
 pub mod logger;
 pub mod config;
@@ -49,3 +52,4 @@ pub mod test_utils;
 /// machine errors and used in fuzz targets and tests.
 #[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
 pub mod enforcing_trait_impls;
+
index 7902a5271a6bcab604627fcd454634fd479ec2a9..f9dfd1b0320cb8308d91489928f3bdc1503504e0 100644 (file)
@@ -32,6 +32,16 @@ pub fn block_from_scid(short_channel_id: &u64) -> u32 {
        return (short_channel_id >> 40) as u32;
 }
 
+/// Extracts the tx index (bytes [2..4]) from the `short_channel_id`
+pub fn tx_index_from_scid(short_channel_id: &u64) -> u32 {
+       return ((short_channel_id >> 16) & MAX_SCID_TX_INDEX) as u32;
+}
+
+/// Extracts the vout (bytes [0..2]) from the `short_channel_id`
+pub fn vout_from_scid(short_channel_id: &u64) -> u16 {
+       return ((short_channel_id) & MAX_SCID_VOUT_INDEX) as u16;
+}
+
 /// Constructs a `short_channel_id` using the components pieces. Results in an error
 /// if the block height, tx index, or vout index overflow the maximum sizes.
 pub fn scid_from_parts(block: u64, tx_index: u64, vout_index: u64) -> Result<u64, ShortChannelIdError> {
@@ -50,6 +60,176 @@ pub fn scid_from_parts(block: u64, tx_index: u64, vout_index: u64) -> Result<u64
        Ok((block << 40) | (tx_index << 16) | vout_index)
 }
 
+/// LDK has multiple reasons to generate fake short channel ids:
+/// 1) zero-conf channels that don't have a confirmed channel id yet
+/// 2) phantom node payments, to get an scid for the phantom node's phantom channel
+pub(crate) mod fake_scid {
+       use bitcoin::hash_types::BlockHash;
+       use bitcoin::hashes::hex::FromHex;
+       use chain::keysinterface::{Sign, KeysInterface};
+       use util::chacha20::ChaCha20;
+       use util::scid_utils;
+
+       use core::convert::TryInto;
+       use core::ops::Deref;
+
+       const TEST_SEGWIT_ACTIVATION_HEIGHT: u32 = 0;
+       const MAINNET_SEGWIT_ACTIVATION_HEIGHT: u32 = 481_824;
+       const MAX_TX_INDEX: u32 = 2_500;
+       const MAX_NAMESPACES: u8 = 8; // We allocate 3 bits for the namespace identifier.
+       const NAMESPACE_ID_BITMASK: u8 = 0b111;
+
+       /// Fake scids are divided into namespaces, with each namespace having its own identifier between
+       /// [0..7]. This allows us to identify what namespace a fake scid corresponds to upon HTLC
+       /// receipt, and handle the HTLC accordingly. The namespace identifier is encrypted when encoded
+       /// into the fake scid.
+       #[derive(Copy, Clone)]
+       pub(super) enum Namespace {
+               Phantom,
+               // Coming soon: a variant for the zero-conf scid namespace
+       }
+
+       impl Namespace {
+               /// We generate "realistic-looking" random scids here, meaning the scid's block height is
+               /// between segwit activation and the current best known height, and the tx index and output
+               /// index are also selected from a "reasonable" range. We add this logic because it makes it
+               /// non-obvious at a glance that the scid is fake, e.g. if it appears in invoice route hints.
+               pub(super) fn get_fake_scid<Signer: Sign, K: Deref>(&self, highest_seen_blockheight: u32, genesis_hash: &BlockHash, fake_scid_rand_bytes: &[u8; 32], keys_manager: &K) -> u64
+                       where K::Target: KeysInterface<Signer = Signer>,
+               {
+                       // Ensure we haven't created a namespace that doesn't fit into the 3 bits we've allocated for
+                       // namespaces.
+                       assert!((*self as u8) < MAX_NAMESPACES);
+                       const BLOCKS_PER_MONTH: u32 = 144 /* blocks per day */ * 30 /* days per month */;
+                       let rand_bytes = keys_manager.get_secure_random_bytes();
+
+                       let segwit_activation_height = segwit_activation_height(genesis_hash);
+                       let mut valid_block_range = if highest_seen_blockheight > segwit_activation_height {
+                               highest_seen_blockheight - segwit_activation_height
+                       } else {
+                               1
+                       };
+                       // We want to ensure that this fake channel won't conflict with any transactions we haven't
+                       // seen yet, in case `highest_seen_blockheight` is updated before we get full information
+                       // about transactions confirmed in the given block.
+                       if valid_block_range > BLOCKS_PER_MONTH { valid_block_range -= BLOCKS_PER_MONTH; }
+
+                       let rand_for_height = u32::from_be_bytes(rand_bytes[..4].try_into().unwrap());
+                       let fake_scid_height = segwit_activation_height + rand_for_height % valid_block_range;
+
+                       let rand_for_tx_index = u32::from_be_bytes(rand_bytes[4..8].try_into().unwrap());
+                       let fake_scid_tx_index = rand_for_tx_index % MAX_TX_INDEX;
+
+                       // Put the scid in the given namespace.
+                       let fake_scid_vout = self.get_encrypted_vout(fake_scid_height, fake_scid_tx_index, fake_scid_rand_bytes);
+                       scid_utils::scid_from_parts(fake_scid_height as u64, fake_scid_tx_index as u64, fake_scid_vout as u64).unwrap()
+               }
+
+               /// We want to ensure that a 3rd party can't identify a payment as belong to a given
+               /// `Namespace`. Therefore, we encrypt it using a random bytes provided by `ChannelManager`.
+               fn get_encrypted_vout(&self, block_height: u32, tx_index: u32, fake_scid_rand_bytes: &[u8; 32]) -> u8 {
+                       let mut salt = [0 as u8; 8];
+                       let block_height_bytes = block_height.to_be_bytes();
+                       salt[0..4].copy_from_slice(&block_height_bytes);
+                       let tx_index_bytes = tx_index.to_be_bytes();
+                       salt[4..8].copy_from_slice(&tx_index_bytes);
+
+                       let mut chacha = ChaCha20::new(fake_scid_rand_bytes, &salt);
+                       let mut vout_byte = [*self as u8];
+                       chacha.process_in_place(&mut vout_byte);
+                       vout_byte[0] & NAMESPACE_ID_BITMASK
+               }
+       }
+
+       pub fn get_phantom_scid<Signer: Sign, K: Deref>(fake_scid_rand_bytes: &[u8; 32], highest_seen_blockheight: u32, genesis_hash: &BlockHash, keys_manager: &K) -> u64
+               where K::Target: KeysInterface<Signer = Signer>,
+       {
+               let namespace = Namespace::Phantom;
+               namespace.get_fake_scid(highest_seen_blockheight, genesis_hash, fake_scid_rand_bytes, keys_manager)
+       }
+
+       fn segwit_activation_height(genesis: &BlockHash) -> u32 {
+               const MAINNET_GENESIS_STR: &'static str = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
+               if BlockHash::from_hex(MAINNET_GENESIS_STR).unwrap() == *genesis {
+                       MAINNET_SEGWIT_ACTIVATION_HEIGHT
+               } else {
+                       TEST_SEGWIT_ACTIVATION_HEIGHT
+               }
+       }
+
+       /// Returns whether the given fake scid falls into the given namespace.
+       pub fn is_valid_phantom(fake_scid_rand_bytes: &[u8; 32], scid: u64) -> bool {
+               let block_height = scid_utils::block_from_scid(&scid);
+               let tx_index = scid_utils::tx_index_from_scid(&scid);
+               let namespace = Namespace::Phantom;
+               let valid_vout = namespace.get_encrypted_vout(block_height, tx_index, fake_scid_rand_bytes);
+               valid_vout == scid_utils::vout_from_scid(&scid) as u8
+       }
+
+       #[cfg(test)]
+       mod tests {
+               use bitcoin::blockdata::constants::genesis_block;
+               use bitcoin::network::constants::Network;
+               use util::scid_utils::fake_scid::{is_valid_phantom, MAINNET_SEGWIT_ACTIVATION_HEIGHT, MAX_TX_INDEX, MAX_NAMESPACES, Namespace, NAMESPACE_ID_BITMASK, segwit_activation_height, TEST_SEGWIT_ACTIVATION_HEIGHT};
+               use util::scid_utils;
+               use util::test_utils;
+               use sync::Arc;
+
+               #[test]
+               fn namespace_identifier_is_within_range() {
+                       let phantom_namespace = Namespace::Phantom;
+                       assert!((phantom_namespace as u8) < MAX_NAMESPACES);
+                       assert!((phantom_namespace as u8) <= NAMESPACE_ID_BITMASK);
+               }
+
+               #[test]
+               fn test_segwit_activation_height() {
+                       let mainnet_genesis = genesis_block(Network::Bitcoin).header.block_hash();
+                       assert_eq!(segwit_activation_height(&mainnet_genesis), MAINNET_SEGWIT_ACTIVATION_HEIGHT);
+
+                       let testnet_genesis = genesis_block(Network::Testnet).header.block_hash();
+                       assert_eq!(segwit_activation_height(&testnet_genesis), TEST_SEGWIT_ACTIVATION_HEIGHT);
+
+                       let signet_genesis = genesis_block(Network::Signet).header.block_hash();
+                       assert_eq!(segwit_activation_height(&signet_genesis), TEST_SEGWIT_ACTIVATION_HEIGHT);
+
+                       let regtest_genesis = genesis_block(Network::Regtest).header.block_hash();
+                       assert_eq!(segwit_activation_height(&regtest_genesis), TEST_SEGWIT_ACTIVATION_HEIGHT);
+               }
+
+               #[test]
+               fn test_is_valid_phantom() {
+                       let namespace = Namespace::Phantom;
+                       let fake_scid_rand_bytes = [0; 32];
+                       let valid_encrypted_vout = namespace.get_encrypted_vout(0, 0, &fake_scid_rand_bytes);
+                       let valid_fake_scid = scid_utils::scid_from_parts(0, 0, valid_encrypted_vout as u64).unwrap();
+                       assert!(is_valid_phantom(&fake_scid_rand_bytes, valid_fake_scid));
+                       let invalid_fake_scid = scid_utils::scid_from_parts(0, 0, 12).unwrap();
+                       assert!(!is_valid_phantom(&fake_scid_rand_bytes, invalid_fake_scid));
+               }
+
+               #[test]
+               fn test_get_fake_scid() {
+                       let mainnet_genesis = genesis_block(Network::Bitcoin).header.block_hash();
+                       let seed = [0; 32];
+                       let fake_scid_rand_bytes = [1; 32];
+                       let keys_manager = Arc::new(test_utils::TestKeysInterface::new(&seed, Network::Testnet));
+                       let namespace = Namespace::Phantom;
+                       let fake_scid = namespace.get_fake_scid(500_000, &mainnet_genesis, &fake_scid_rand_bytes, &keys_manager);
+
+                       let fake_height = scid_utils::block_from_scid(&fake_scid);
+                       assert!(fake_height >= MAINNET_SEGWIT_ACTIVATION_HEIGHT);
+                       assert!(fake_height <= 500_000);
+
+                       let fake_tx_index = scid_utils::tx_index_from_scid(&fake_scid);
+                       assert!(fake_tx_index <= MAX_TX_INDEX);
+
+                       let fake_vout = scid_utils::vout_from_scid(&fake_scid);
+                       assert!(fake_vout < MAX_NAMESPACES as u16);
+               }
+       }
+}
+
 #[cfg(test)]
 mod tests {
        use super::*;
@@ -63,6 +243,24 @@ mod tests {
                assert_eq!(block_from_scid(&0xffffff_ffffff_ffff), 0xffffff);
        }
 
+       #[test]
+       fn test_tx_index_from_scid() {
+               assert_eq!(tx_index_from_scid(&0x000000_000000_0000), 0);
+               assert_eq!(tx_index_from_scid(&0x000000_000001_0000), 1);
+               assert_eq!(tx_index_from_scid(&0xffffff_000001_ffff), 1);
+               assert_eq!(tx_index_from_scid(&0xffffff_800000_ffff), 0x800000);
+               assert_eq!(tx_index_from_scid(&0xffffff_ffffff_ffff), 0xffffff);
+       }
+
+       #[test]
+       fn test_vout_from_scid() {
+               assert_eq!(vout_from_scid(&0x000000_000000_0000), 0);
+               assert_eq!(vout_from_scid(&0x000000_000000_0001), 1);
+               assert_eq!(vout_from_scid(&0xffffff_ffffff_0001), 1);
+               assert_eq!(vout_from_scid(&0xffffff_ffffff_8000), 0x8000);
+               assert_eq!(vout_from_scid(&0xffffff_ffffff_ffff), 0xffff);
+       }
+
        #[test]
        fn test_scid_from_parts() {
                assert_eq!(scid_from_parts(0x00000000, 0x00000000, 0x0000).unwrap(), 0x000000_000000_0000);
index 0e1c92d009c112cbc74c6f811a5fc9d158f56dd3..9cd2f9ec13e8bd49de15be2fc0f283fe34660eee 100644 (file)
@@ -47,7 +47,7 @@ use sync::{Mutex, Arc};
 use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
 use core::{cmp, mem};
 use bitcoin::bech32::u5;
-use chain::keysinterface::{InMemorySigner, KeyMaterial};
+use chain::keysinterface::{InMemorySigner, Recipient, KeyMaterial};
 
 pub struct TestVecWriter(pub Vec<u8>);
 impl Writer for TestVecWriter {
@@ -70,7 +70,7 @@ pub struct OnlyReadsKeysInterface {}
 impl keysinterface::KeysInterface for OnlyReadsKeysInterface {
        type Signer = EnforcingSigner;
 
-       fn get_node_secret(&self) -> SecretKey { unreachable!(); }
+       fn get_node_secret(&self, _recipient: Recipient) -> Result<SecretKey, ()> { unreachable!(); }
        fn get_inbound_payment_key_material(&self) -> KeyMaterial { unreachable!(); }
        fn get_destination_script(&self) -> Script { unreachable!(); }
        fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { unreachable!(); }
@@ -88,7 +88,7 @@ impl keysinterface::KeysInterface for OnlyReadsKeysInterface {
                        false
                ))
        }
-       fn sign_invoice(&self, _hrp_bytes: &[u8], _invoice_data: &[u5]) -> Result<RecoverableSignature, ()> { unreachable!(); }
+       fn sign_invoice(&self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient) -> Result<RecoverableSignature, ()> { unreachable!(); }
 }
 
 pub struct TestChainMonitor<'a> {
@@ -470,7 +470,7 @@ impl Logger for TestLogger {
 }
 
 pub struct TestKeysInterface {
-       pub backing: keysinterface::KeysManager,
+       pub backing: keysinterface::PhantomKeysManager,
        pub override_session_priv: Mutex<Option<[u8; 32]>>,
        pub override_channel_id_priv: Mutex<Option<[u8; 32]>>,
        pub disable_revocation_policy_check: bool,
@@ -481,8 +481,12 @@ pub struct TestKeysInterface {
 impl keysinterface::KeysInterface for TestKeysInterface {
        type Signer = EnforcingSigner;
 
-       fn get_node_secret(&self) -> SecretKey { self.backing.get_node_secret() }
-       fn get_inbound_payment_key_material(&self) -> keysinterface::KeyMaterial { self.backing.get_inbound_payment_key_material() }
+       fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()> {
+               self.backing.get_node_secret(recipient)
+       }
+       fn get_inbound_payment_key_material(&self) -> keysinterface::KeyMaterial {
+               self.backing.get_inbound_payment_key_material()
+       }
        fn get_destination_script(&self) -> Script { self.backing.get_destination_script() }
 
        fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
@@ -519,7 +523,7 @@ impl keysinterface::KeysInterface for TestKeysInterface {
        fn read_chan_signer(&self, buffer: &[u8]) -> Result<Self::Signer, msgs::DecodeError> {
                let mut reader = io::Cursor::new(buffer);
 
-               let inner: InMemorySigner = ReadableArgs::read(&mut reader, self.get_node_secret())?;
+               let inner: InMemorySigner = ReadableArgs::read(&mut reader, self.get_node_secret(Recipient::Node).unwrap())?;
                let state = self.make_enforcement_state_cell(inner.commitment_seed);
 
                Ok(EnforcingSigner::new_with_revoked(
@@ -529,8 +533,8 @@ impl keysinterface::KeysInterface for TestKeysInterface {
                ))
        }
 
-       fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5]) -> Result<RecoverableSignature, ()> {
-               self.backing.sign_invoice(hrp_bytes, invoice_data)
+       fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
+               self.backing.sign_invoice(hrp_bytes, invoice_data, recipient)
        }
 }
 
@@ -538,7 +542,7 @@ impl TestKeysInterface {
        pub fn new(seed: &[u8; 32], network: Network) -> Self {
                let now = Duration::from_secs(genesis_block(network).header.time as u64);
                Self {
-                       backing: keysinterface::KeysManager::new(seed, now.as_secs(), now.subsec_nanos()),
+                       backing: keysinterface::PhantomKeysManager::new(seed, now.as_secs(), now.subsec_nanos(), seed),
                        override_session_priv: Mutex::new(None),
                        override_channel_id_priv: Mutex::new(None),
                        disable_revocation_policy_check: false,