X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fkeysinterface.rs;h=9a3baea8bb442a3c6928cca83559606a66de95ff;hb=28c9b56113ff1ebb1b505a2c979c55c1626aa06b;hp=dffe060d91bb71a5f33e8c023de6cac94fb9826d;hpb=d62edd58abfca1929fc3e67eaef0b40ed66fe617;p=rust-lightning diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index dffe060d..9a3baea8 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -11,12 +11,12 @@ //! spendable on-chain outputs which the user owns and is responsible for using just as any other //! on-chain output which is theirs. -use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, SigHashType}; +use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, EcdsaSighashType}; use bitcoin::blockdata::script::{Script, Builder}; use bitcoin::blockdata::opcodes; use bitcoin::network::constants::Network; use bitcoin::util::bip32::{ExtendedPrivKey, ExtendedPubKey, ChildNumber}; -use bitcoin::util::bip143; +use bitcoin::util::sighash; use bitcoin::bech32::u5; use bitcoin::hashes::{Hash, HashEngine}; @@ -25,12 +25,14 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hash_types::WPubkeyHash; -use bitcoin::secp256k1::key::{SecretKey, PublicKey}; -use bitcoin::secp256k1::{Secp256k1, Signature, Signing}; -use bitcoin::secp256k1::recovery::RecoverableSignature; -use bitcoin::secp256k1; +use bitcoin::secp256k1::{SecretKey, PublicKey}; +use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Signing}; +use bitcoin::secp256k1::ecdh::SharedSecret; +use bitcoin::secp256k1::ecdsa::RecoverableSignature; +use bitcoin::{secp256k1, Witness}; use util::{byte_utils, transaction_utils}; +use util::crypto::{hkdf_extract_expand_twice, sign}; use util::ser::{Writeable, Writer, Readable, ReadableArgs}; use chain::transaction::OutPoint; @@ -379,15 +381,36 @@ 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 based on the provided [`Recipient`]. /// - /// This method must return the same value each time it is called. - fn get_node_secret(&self) -> SecretKey; + /// The node_id/network_key is the public key that corresponds to this secret key. + /// + /// 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; + /// Gets the ECDH shared secret of our [`node secret`] and `other_key`, multiplying by `tweak` if + /// one is provided. Note that this tweak can be applied to `other_key` instead of our node + /// secret, though this is less efficient. + /// + /// [`node secret`]: Self::get_node_secret + fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&[u8; 32]>) -> Result; /// 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 +446,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; + /// + /// 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; /// 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; } @@ -563,16 +597,16 @@ impl InMemorySigner { if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); } let remotepubkey = self.pubkeys().payment_point; - let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Testnet).script_pubkey(); - let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]); - let remotesig = secp_ctx.sign(&sighash, &self.payment_key); - let payment_script = bitcoin::Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Bitcoin).unwrap().script_pubkey(); + let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Testnet).script_pubkey(); + let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]); + let remotesig = sign(secp_ctx, &sighash, &self.payment_key); + let payment_script = bitcoin::Address::p2wpkh(&::bitcoin::PublicKey{compressed: true, inner: remotepubkey}, Network::Bitcoin).unwrap().script_pubkey(); if payment_script != descriptor.output.script_pubkey { return Err(()); } let mut witness = Vec::with_capacity(2); witness.push(remotesig.serialize_der().to_vec()); - witness[0].push(SigHashType::All as u8); + witness[0].push(EcdsaSighashType::All as u8); witness.push(remotepubkey.serialize().to_vec()); Ok(witness) } @@ -598,15 +632,15 @@ impl InMemorySigner { .expect("We constructed the payment_base_key, so we can only fail here if the RNG is busted."); let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key); let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey); - let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]); - let local_delayedsig = secp_ctx.sign(&sighash, &delayed_payment_key); + let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]); + let local_delayedsig = sign(secp_ctx, &sighash, &delayed_payment_key); let payment_script = bitcoin::Address::p2wsh(&witness_script, Network::Bitcoin).script_pubkey(); if descriptor.output.script_pubkey != payment_script { return Err(()); } let mut witness = Vec::with_capacity(3); witness.push(local_delayedsig.serialize_der().to_vec()); - witness[0].push(SigHashType::All as u8); + witness[0].push(EcdsaSighashType::All as u8); witness.push(vec!()); //MINIMALIF witness.push(witness_script.clone().into_bytes()); Ok(witness) @@ -645,10 +679,10 @@ impl BaseSign for InMemorySigner { for htlc in commitment_tx.htlcs() { let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_tx.feerate_per_kw(), self.holder_selected_contest_delay(), htlc, self.opt_anchors(), &keys.broadcaster_delayed_payment_key, &keys.revocation_key); let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, self.opt_anchors(), &keys); - let htlc_sighashtype = if self.opt_anchors() { SigHashType::SinglePlusAnyoneCanPay } else { SigHashType::All }; - let htlc_sighash = hash_to_message!(&bip143::SigHashCache::new(&htlc_tx).signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype)[..]); + let htlc_sighashtype = if self.opt_anchors() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All }; + let htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype).unwrap()[..]); let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key).map_err(|_| ())?; - htlc_sigs.push(secp_ctx.sign(&htlc_sighash, &holder_htlc_key)); + htlc_sigs.push(sign(secp_ctx, &htlc_sighash, &holder_htlc_key)); } Ok((commitment_sig, htlc_sigs)) @@ -687,9 +721,9 @@ impl BaseSign for InMemorySigner { let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint).map_err(|_| ())?; chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.holder_selected_contest_delay(), &counterparty_delayedpubkey) }; - let mut sighash_parts = bip143::SigHashCache::new(justice_tx); - let sighash = hash_to_message!(&sighash_parts.signature_hash(input, &witness_script, amount, SigHashType::All)[..]); - return Ok(secp_ctx.sign(&sighash, &revocation_key)) + let mut sighash_parts = sighash::SighashCache::new(justice_tx); + let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]); + return Ok(sign(secp_ctx, &sighash, &revocation_key)) } fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1) -> Result { @@ -701,9 +735,9 @@ impl BaseSign for InMemorySigner { let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint).map_err(|_| ())?; chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey) }; - let mut sighash_parts = bip143::SigHashCache::new(justice_tx); - let sighash = hash_to_message!(&sighash_parts.signature_hash(input, &witness_script, amount, SigHashType::All)[..]); - return Ok(secp_ctx.sign(&sighash, &revocation_key)) + let mut sighash_parts = sighash::SighashCache::new(justice_tx); + let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]); + return Ok(sign(secp_ctx, &sighash, &revocation_key)) } fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1) -> Result { @@ -715,9 +749,9 @@ impl BaseSign for InMemorySigner { } else { return Err(()) } } else { return Err(()) } } else { return Err(()) }; - let mut sighash_parts = bip143::SigHashCache::new(htlc_tx); - let sighash = hash_to_message!(&sighash_parts.signature_hash(input, &witness_script, amount, SigHashType::All)[..]); - return Ok(secp_ctx.sign(&sighash, &htlc_key)) + let mut sighash_parts = sighash::SighashCache::new(htlc_tx); + let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]); + return Ok(sign(secp_ctx, &sighash, &htlc_key)) } Err(()) } @@ -731,7 +765,7 @@ impl BaseSign for InMemorySigner { fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1) -> Result<(Signature, Signature), ()> { let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]); - Ok((secp_ctx.sign(&msghash, &self.node_secret), secp_ctx.sign(&msghash, &self.funding_key))) + Ok((sign(secp_ctx, &msghash, &self.node_secret), sign(secp_ctx, &msghash, &self.funding_key))) } fn ready_channel(&mut self, channel_parameters: &ChannelTransactionParameters) { @@ -810,6 +844,12 @@ impl ReadableArgs 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, node_secret: SecretKey, @@ -853,10 +893,10 @@ impl KeysManager { // Note that when we aren't serializing the key, network doesn't matter match ExtendedPrivKey::new_master(Network::Testnet, seed) { Ok(master_key) => { - let node_secret = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(0).unwrap()).expect("Your RNG is busted").private_key.key; + let node_secret = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(0).unwrap()).expect("Your RNG is busted").private_key; let destination_script = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(1).unwrap()) { Ok(destination_key) => { - let wpubkey_hash = WPubkeyHash::hash(&ExtendedPubKey::from_private(&secp_ctx, &destination_key).public_key.to_bytes()); + let wpubkey_hash = WPubkeyHash::hash(&ExtendedPubKey::from_priv(&secp_ctx, &destination_key).to_pub().to_bytes()); Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0) .push_slice(&wpubkey_hash.into_inner()) .into_script() @@ -864,12 +904,12 @@ impl KeysManager { Err(_) => panic!("Your RNG is busted"), }; let shutdown_pubkey = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(2).unwrap()) { - Ok(shutdown_key) => ExtendedPubKey::from_private(&secp_ctx, &shutdown_key).public_key.key, + Ok(shutdown_key) => ExtendedPubKey::from_priv(&secp_ctx, &shutdown_key).public_key, Err(_) => panic!("Your RNG is busted"), }; let channel_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(3).unwrap()).expect("Your RNG is busted"); let rand_bytes_master_key = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(4).unwrap()).expect("Your RNG is busted"); - let inbound_payment_key: SecretKey = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(5).unwrap()).expect("Your RNG is busted").private_key.key; + let inbound_payment_key: SecretKey = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(5).unwrap()).expect("Your RNG is busted").private_key; let mut inbound_pmt_key_bytes = [0; 32]; inbound_pmt_key_bytes.copy_from_slice(&inbound_payment_key[..]); @@ -920,7 +960,7 @@ impl KeysManager { // entropy, everything else just ensures uniqueness. We rely on the unique_start (ie // starting_time provided in the constructor) to be unique. let child_privkey = self.channel_master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(chan_id as u32).expect("key space exhausted")).expect("Your RNG is busted"); - unique_start.input(&child_privkey.private_key.key[..]); + unique_start.input(&child_privkey.private_key[..]); let seed = Sha256::from_engine(unique_start).into_inner(); @@ -964,7 +1004,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. @@ -983,7 +1023,7 @@ impl KeysManager { previous_output: descriptor.outpoint.into_bitcoin_outpoint(), script_sig: Script::new(), sequence: 0, - witness: Vec::new(), + witness: Witness::new(), }); witness_weight += StaticPaymentOutputDescriptor::MAX_WITNESS_LENGTH; input_value += descriptor.output.value; @@ -994,7 +1034,7 @@ impl KeysManager { previous_output: descriptor.outpoint.into_bitcoin_outpoint(), script_sig: Script::new(), sequence: descriptor.to_self_delay as u32, - witness: Vec::new(), + witness: Witness::new(), }); witness_weight += DelayedPaymentOutputDescriptor::MAX_WITNESS_LENGTH; input_value += descriptor.output.value; @@ -1005,7 +1045,7 @@ impl KeysManager { previous_output: outpoint.into_bitcoin_outpoint(), script_sig: Script::new(), sequence: 0, - witness: Vec::new(), + witness: Witness::new(), }); witness_weight += 1 + 73 + 34; input_value += output.value; @@ -1033,7 +1073,7 @@ impl KeysManager { self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id), descriptor.channel_keys_id)); } - spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?; + spend_tx.input[input_idx].witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?); }, SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => { if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id { @@ -1041,7 +1081,7 @@ impl KeysManager { self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id), descriptor.channel_keys_id)); } - spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?; + spend_tx.input[input_idx].witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?); }, SpendableOutputDescriptor::StaticOutput { ref output, .. } => { let derivation_idx = if output.script_pubkey == self.destination_script { @@ -1061,29 +1101,30 @@ impl KeysManager { Err(_) => panic!("Your rng is busted"), } }; - let pubkey = ExtendedPubKey::from_private(&secp_ctx, &secret).public_key; + let pubkey = ExtendedPubKey::from_priv(&secp_ctx, &secret).to_pub(); if derivation_idx == 2 { - assert_eq!(pubkey.key, self.shutdown_pubkey); + assert_eq!(pubkey.inner, self.shutdown_pubkey); } let witness_script = bitcoin::Address::p2pkh(&pubkey, Network::Testnet).script_pubkey(); let payment_script = bitcoin::Address::p2wpkh(&pubkey, Network::Testnet).expect("uncompressed key found").script_pubkey(); if payment_script != output.script_pubkey { return Err(()); }; - let sighash = hash_to_message!(&bip143::SigHashCache::new(&spend_tx).signature_hash(input_idx, &witness_script, output.value, SigHashType::All)[..]); - let sig = secp_ctx.sign(&sighash, &secret.private_key.key); - spend_tx.input[input_idx].witness.push(sig.serialize_der().to_vec()); - spend_tx.input[input_idx].witness[0].push(SigHashType::All as u8); - spend_tx.input[input_idx].witness.push(pubkey.key.serialize().to_vec()); + let sighash = hash_to_message!(&sighash::SighashCache::new(&spend_tx).segwit_signature_hash(input_idx, &witness_script, output.value, EcdsaSighashType::All).unwrap()[..]); + let sig = sign(secp_ctx, &sighash, &secret.private_key); + let mut sig_ser = sig.serialize_der().to_vec(); + sig_ser.push(EcdsaSighashType::All as u8); + spend_tx.input[input_idx].witness.push(sig_ser); + spend_tx.input[input_idx].witness.push(pubkey.inner.serialize().to_vec()); }, } input_idx += 1; } - debug_assert!(expected_max_weight >= spend_tx.get_weight()); + debug_assert!(expected_max_weight >= spend_tx.weight()); // Note that witnesses with a signature vary somewhat in size, so allow // `expected_max_weight` to overshoot by up to 3 bytes per input. - debug_assert!(expected_max_weight <= spend_tx.get_weight() + descriptors.len() * 3); + debug_assert!(expected_max_weight <= spend_tx.weight() + descriptors.len() * 3); Ok(spend_tx) } @@ -1092,8 +1133,19 @@ 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 { + match recipient { + Recipient::Node => Ok(self.node_secret.clone()), + Recipient::PhantomNode => Err(()) + } + } + + fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&[u8; 32]>) -> Result { + let mut node_secret = self.get_node_secret(recipient)?; + if let Some(tweak) = tweak { + node_secret.mul_assign(tweak).map_err(|_| ())?; + } + Ok(SharedSecret::new(other_key, &node_secret)) } fn get_inbound_payment_key_material(&self) -> KeyMaterial { @@ -1123,19 +1175,131 @@ impl KeysInterface for KeysManager { let child_ix = self.rand_bytes_child_index.fetch_add(1, Ordering::AcqRel); let child_privkey = self.rand_bytes_master_key.ckd_priv(&self.secp_ctx, ChildNumber::from_hardened_idx(child_ix as u32).expect("key space exhausted")).expect("Your RNG is busted"); - sha.input(&child_privkey.private_key.key[..]); + sha.input(&child_privkey.private_key[..]); sha.input(b"Unique Secure Random Bytes Salt"); Sha256::from_engine(sha).into_inner() } fn read_chan_signer(&self, reader: &[u8]) -> Result { - 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 { + 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_ecdsa_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 { + match recipient { + Recipient::Node => self.inner.get_node_secret(Recipient::Node), + Recipient::PhantomNode => Ok(self.phantom_secret.clone()), + } + } + + fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&[u8; 32]>) -> Result { + let mut node_secret = self.get_node_secret(recipient)?; + if let Some(tweak) = tweak { + node_secret.mul_assign(tweak).map_err(|_| ())?; + } + Ok(SharedSecret::new(other_key, &node_secret)) + } + + 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.inner.read_chan_signer(reader) } - fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5]) -> Result { + fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result { 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_ecdsa_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(&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec, change_destination_script: Script, feerate_sat_per_1000_weight: u32, secp_ctx: &Secp256k1) -> Result { + 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) } }