Skip to content

Commit 771a6d8

Browse files
keysmanager: support phantom payments with PhantomKeysManager
To support the feature of generating invoices that can be paid to any of multiple nodes, a key manager need to be able to share an inbound_payment_key and phantom secret key. This is because a phantom payment may be received by any node participating in the invoice, so all nodes must be able to decrypt the phantom payment (and therefore must share decryption key(s)) in the act of pretending to be the phantom node. Thus we add a new `PhantomKeysManager` that supports these features. To be more specific, the inbound payment key must be shared because it is used to decrypt the payment details for verification (LDK avoids storing inbound payment data by encrypting payment metadata in the payment hash and/or payment secret). The phantom secret must be shared because enables any real node included in the phantom invoice to decrypt the final layer of the onion packet, since the onion is encrypted by the sender using the phantom public key provided in the invoice.
1 parent 03c9ccb commit 771a6d8

File tree

1 file changed

+113
-1
lines changed

1 file changed

+113
-1
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use bitcoin::secp256k1::recovery::RecoverableSignature;
3131
use bitcoin::secp256k1;
3232

3333
use util::{byte_utils, transaction_utils};
34+
use util::crypto::hkdf_extract_expand_twice;
3435
use util::ser::{Writeable, Writer, Readable, ReadableArgs};
3536

3637
use chain::transaction::OutPoint;
@@ -427,7 +428,16 @@ pub trait KeysInterface {
427428

428429
/// Get secret key material as bytes for use in encrypting and decrypting inbound payment data.
429430
///
431+
/// If the implementor of this trait supports [phantom node payments], then every node that is
432+
/// intended to be included in the phantom invoice route hints must return the same value from
433+
/// this method.
434+
// This is because LDK avoids storing inbound payment data by encrypting payment data in the
435+
// payment hash and/or payment secret, therefore for a payment to be receivable by multiple
436+
// nodes, they must share the key that encrypts this payment data.
437+
///
430438
/// This method must return the same value each time it is called.
439+
///
440+
/// [phantom node payments]: PhantomKeysManager
431441
fn get_inbound_payment_key_material(&self) -> KeyMaterial;
432442
}
433443

@@ -810,6 +820,12 @@ impl ReadableArgs<SecretKey> for InMemorySigner {
810820
/// ChannelMonitor closes may use seed/1'
811821
/// Cooperative closes may use seed/2'
812822
/// The two close keys may be needed to claim on-chain funds!
823+
///
824+
/// This struct cannot be used for nodes that wish to support receiving phantom payments;
825+
/// [`PhantomKeysManager`] must be used instead.
826+
///
827+
/// Note that switching between this struct and [`PhantomKeysManager`] will invalidate any
828+
/// previously issued invoices and attempts to pay previous invoices will fail.
813829
pub struct KeysManager {
814830
secp_ctx: Secp256k1<secp256k1::All>,
815831
node_secret: SecretKey,
@@ -964,7 +980,7 @@ impl KeysManager {
964980
/// transaction will have a feerate, at least, of the given value.
965981
///
966982
/// Returns `Err(())` if the output value is greater than the input value minus required fee,
967-
/// if a descriptor was duplicated, or if an output descriptor script_pubkey
983+
/// if a descriptor was duplicated, or if an output descriptor `script_pubkey`
968984
/// does not match the one we can spend.
969985
///
970986
/// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
@@ -1139,6 +1155,102 @@ impl KeysInterface for KeysManager {
11391155
}
11401156
}
11411157

1158+
/// Similar to [`KeysManager`], but allows the node using this struct to receive phantom node
1159+
/// payments.
1160+
///
1161+
/// A phantom node payment is a payment made to a phantom invoice, which is an invoice that can be
1162+
/// paid to one of multiple nodes. This works because we encode the invoice route hints such that
1163+
/// LDK will recognize an incoming payment as destined for a phantom node, and collect the payment
1164+
/// itself without ever needing to forward to this fake node.
1165+
///
1166+
/// Phantom node payments are useful for load balancing between multiple LDK nodes. They also
1167+
/// provide some fault tolerance, because payers will automatically retry paying other provided
1168+
/// nodes in the case that one node goes down.
1169+
///
1170+
/// Note that multi-path payments are not supported in phantom invoices for security reasons.
1171+
// In the hypothetical case that we did support MPP phantom payments, there would be no way for
1172+
// nodes to know when the full payment has been received (and the preimage can be released) without
1173+
// significantly compromising on our safety guarantees. I.e., if we expose the ability for the user
1174+
// to tell LDK when the preimage can be released, we open ourselves to attacks where the preimage
1175+
// is released too early.
1176+
//
1177+
/// Switching between this struct and [`KeysManager`] will invalidate any previously issued
1178+
/// invoices and attempts to pay previous invoices will fail.
1179+
pub struct PhantomKeysManager {
1180+
inner: KeysManager,
1181+
inbound_payment_key: KeyMaterial,
1182+
phantom_secret: SecretKey,
1183+
}
1184+
1185+
impl KeysInterface for PhantomKeysManager {
1186+
type Signer = InMemorySigner;
1187+
1188+
fn get_node_secret(&self) -> SecretKey {
1189+
self.inner.get_node_secret()
1190+
}
1191+
1192+
fn get_inbound_payment_key_material(&self) -> KeyMaterial {
1193+
self.inbound_payment_key.clone()
1194+
}
1195+
1196+
fn get_destination_script(&self) -> Script {
1197+
self.inner.get_destination_script()
1198+
}
1199+
1200+
fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
1201+
self.inner.get_shutdown_scriptpubkey()
1202+
}
1203+
1204+
fn get_channel_signer(&self, inbound: bool, channel_value_satoshis: u64) -> Self::Signer {
1205+
self.inner.get_channel_signer(inbound, channel_value_satoshis)
1206+
}
1207+
1208+
fn get_secure_random_bytes(&self) -> [u8; 32] {
1209+
self.inner.get_secure_random_bytes()
1210+
}
1211+
1212+
fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
1213+
self.inner.read_chan_signer(reader)
1214+
}
1215+
1216+
fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5]) -> Result<RecoverableSignature, ()> {
1217+
let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data);
1218+
Ok(self.inner.secp_ctx.sign_recoverable(&hash_to_message!(&Sha256::hash(&preimage)), &self.get_node_secret()))
1219+
}
1220+
}
1221+
1222+
impl PhantomKeysManager {
1223+
/// Constructs a `PhantomKeysManager` given a 32-byte seed and an additional `cross_node_seed`
1224+
/// that is shared across all nodes that intend to participate in [phantom node payments] together.
1225+
///
1226+
/// See [`KeysManager::new`] for more information on `seed`, `starting_time_secs`, and
1227+
/// `starting_time_nanos`.
1228+
///
1229+
/// `cross_node_seed` must be the same across all phantom payment-receiving nodes and also the
1230+
/// same across restarts, or else inbound payments may fail.
1231+
///
1232+
/// [phantom node payments]: PhantomKeysManager
1233+
pub fn new(seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32, cross_node_seed: &[u8; 32]) -> Self {
1234+
let inner = KeysManager::new(seed, starting_time_secs, starting_time_nanos);
1235+
let (inbound_key, phantom_key) = hkdf_extract_expand_twice(b"LDK Inbound and Phantom Payment Key Expansion", cross_node_seed);
1236+
Self {
1237+
inner,
1238+
inbound_payment_key: KeyMaterial(inbound_key),
1239+
phantom_secret: SecretKey::from_slice(&phantom_key).unwrap(),
1240+
}
1241+
}
1242+
1243+
/// See [`KeysManager::spend_spendable_outputs`] for documentation on this method.
1244+
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, ()> {
1245+
self.inner.spend_spendable_outputs(descriptors, outputs, change_destination_script, feerate_sat_per_1000_weight, secp_ctx)
1246+
}
1247+
1248+
/// See [`KeysManager::derive_channel_keys`] for documentation on this method.
1249+
pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner {
1250+
self.inner.derive_channel_keys(channel_value_satoshis, params)
1251+
}
1252+
}
1253+
11421254
// Ensure that BaseSign can have a vtable
11431255
#[test]
11441256
pub fn dyn_sign() {

0 commit comments

Comments
 (0)