X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fkeysinterface.rs;h=b12eee017aa93b81a080bf342a267c76aa309f5e;hb=384c4dc7753e4b7ac53ea380e52809babd8f0f9b;hp=e1cc90674c0d44865555ee00dcd348ebcd1681c6;hpb=6e19d1f523e8c272060d7ad3b236b05008ff2a8f;p=rust-lightning diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index e1cc9067..b12eee01 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,25 +25,28 @@ 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, Scalar}; +use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Signing}; +use bitcoin::secp256k1::ecdh::SharedSecret; +use bitcoin::secp256k1::ecdsa::RecoverableSignature; +use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness}; -use util::{byte_utils, transaction_utils}; -use util::ser::{Writeable, Writer, Readable}; +use crate::util::{byte_utils, transaction_utils}; +use crate::util::crypto::{hkdf_extract_expand_twice, sign}; +use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs}; -use chain::transaction::OutPoint; -use ln::{chan_utils, PaymentPreimage}; -use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction}; -use ln::msgs::UnsignedChannelAnnouncement; -use ln::script::ShutdownScript; +use crate::chain::transaction::OutPoint; +use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI; +use crate::ln::{chan_utils, PaymentPreimage}; +use crate::ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction}; +use crate::ln::msgs::UnsignedChannelAnnouncement; +use crate::ln::script::ShutdownScript; -use prelude::*; +use crate::prelude::*; use core::sync::atomic::{AtomicUsize, Ordering}; -use io::{self, Error}; -use ln::msgs::{DecodeError, MAX_VALUE_MSAT}; -use util::invoice::construct_invoice_preimage; +use crate::io::{self, Error}; +use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT}; +use crate::util::invoice::construct_invoice_preimage; /// Used as initial key material, to be expanded into multiple secret keys (but not to be used /// directly). This is used within LDK to encrypt/decrypt inbound payment data. @@ -53,7 +56,7 @@ pub struct KeyMaterial(pub [u8; 32]); /// Information about a spendable output to a P2WSH script. See /// SpendableOutputDescriptor::DelayedPaymentOutput for more details on how to spend this. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct DelayedPaymentOutputDescriptor { /// The outpoint which is spendable pub outpoint: OutPoint, @@ -93,7 +96,7 @@ impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, { /// Information about a spendable output to our "payment key". See /// SpendableOutputDescriptor::StaticPaymentOutput for more details on how to spend this. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct StaticPaymentOutputDescriptor { /// The outpoint which is spendable pub outpoint: OutPoint, @@ -124,7 +127,7 @@ impl_writeable_tlv_based!(StaticPaymentOutputDescriptor, { /// spend on-chain. The information needed to do this is provided in this enum, including the /// outpoint describing which txid and output index is available, the full output which exists at /// that txid/index, and any keys or other information required to sign. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum SpendableOutputDescriptor { /// An output to a script which was provided via KeysInterface directly, either from /// `get_destination_script()` or `get_shutdown_scriptpubkey()`, thus you should already know @@ -346,13 +349,23 @@ pub trait BaseSign { /// chosen to forgo their output as dust. fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1) -> Result; - /// Signs a channel announcement message with our funding key, proving it comes from one - /// of the channel participants. + /// Computes the signature for a commitment transaction's anchor output used as an + /// input within `anchor_tx`, which spends the commitment transaction, at index `input`. + fn sign_holder_anchor_input( + &self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1, + ) -> Result; + + /// Signs a channel announcement message with our funding key and our node secret key (aka + /// node_id or network_key), proving it comes from one of the channel participants. + /// + /// The first returned signature should be from our node secret key, the second from our + /// funding key. /// /// Note that if this fails or is rejected, the channel will not be publicly announced and /// our counterparty may (though likely will not) close the channel on us for violating the /// protocol. - fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1) -> Result; + fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1) + -> Result<(Signature, Signature), ()>; /// Set the counterparty static channel data, including basepoints, /// counterparty_selected/holder_selected_contest_delay and funding outpoint. @@ -375,15 +388,53 @@ 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. + /// + /// Errors if the `Recipient` variant is not supported by the implementation. + fn get_node_secret(&self, recipient: Recipient) -> Result; + /// Get node id based on the provided [`Recipient`]. This public key corresponds to the secret in + /// [`get_node_secret`]. + /// + /// This method must return the same value each time it is called with a given `Recipient` + /// parameter. + /// + /// Errors if the `Recipient` variant is not supported by the implementation. + /// + /// [`get_node_secret`]: KeysInterface::get_node_secret + fn get_node_id(&self, recipient: Recipient) -> Result { + let secp_ctx = Secp256k1::signing_only(); + Ok(PublicKey::from_secret_key(&secp_ctx, &self.get_node_secret(recipient)?)) + } + /// 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. + /// + /// Errors if the `Recipient` variant is not supported by the implementation. + /// + /// [`node secret`]: Self::get_node_secret + fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> 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 @@ -419,11 +470,24 @@ 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`]. + /// + /// Errors if the `Recipient` variant is not supported by the implementation. + 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; } @@ -447,6 +511,8 @@ pub struct InMemorySigner { pub commitment_seed: [u8; 32], /// Holder public keys and basepoints pub(crate) holder_channel_pubkeys: ChannelPublicKeys, + /// Private key of our node secret, used for signing channel announcements + node_secret: SecretKey, /// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance channel_parameters: Option, /// The total value of this channel @@ -459,6 +525,7 @@ impl InMemorySigner { /// Create a new InMemorySigner pub fn new( secp_ctx: &Secp256k1, + node_secret: SecretKey, funding_key: SecretKey, revocation_base_key: SecretKey, payment_key: SecretKey, @@ -478,6 +545,7 @@ impl InMemorySigner { delayed_payment_base_key, htlc_base_key, commitment_seed, + node_secret, channel_value_satoshis, holder_channel_pubkeys, channel_parameters: None, @@ -555,16 +623,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) } @@ -584,25 +652,26 @@ impl InMemorySigner { if spend_tx.input.len() <= input_idx { return Err(()); } if !spend_tx.input[input_idx].script_sig.is_empty() { return Err(()); } if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); } - if spend_tx.input[input_idx].sequence != descriptor.to_self_delay as u32 { return Err(()); } + if spend_tx.input[input_idx].sequence.0 != descriptor.to_self_delay as u32 { return Err(()); } let delayed_payment_key = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &self.delayed_payment_base_key) .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) } + } impl BaseSign for InMemorySigner { @@ -637,10 +706,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)) @@ -679,9 +748,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 { @@ -693,9 +762,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 { @@ -707,9 +776,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(()) } @@ -720,9 +789,20 @@ impl BaseSign for InMemorySigner { Ok(closing_tx.trust().sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx)) } - fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1) -> Result { + fn sign_holder_anchor_input( + &self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1, + ) -> Result { + let witness_script = chan_utils::get_anchor_redeemscript(&self.holder_channel_pubkeys.funding_pubkey); + let sighash = sighash::SighashCache::new(&*anchor_tx).segwit_signature_hash( + input, &witness_script, ANCHOR_OUTPUT_VALUE_SATOSHI, EcdsaSighashType::All, + ).unwrap(); + Ok(sign(secp_ctx, &hash_to_message!(&sighash[..]), &self.funding_key)) + } + + 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.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) { @@ -757,8 +837,8 @@ impl Writeable for InMemorySigner { } } -impl Readable for InMemorySigner { - fn read(reader: &mut R) -> Result { +impl ReadableArgs for InMemorySigner { + fn read(reader: &mut R, node_secret: SecretKey) -> Result { let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION); let funding_key = Readable::read(reader)?; @@ -784,6 +864,7 @@ impl Readable for InMemorySigner { payment_key, delayed_payment_base_key, htlc_base_key, + node_secret, commitment_seed, channel_value_satoshis, holder_channel_pubkeys, @@ -800,9 +881,16 @@ impl Readable 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, + node_id: PublicKey, inbound_payment_key: KeyMaterial, destination_script: Script, shutdown_pubkey: PublicKey, @@ -843,10 +931,11 @@ 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 node_id = PublicKey::from_secret_key(&secp_ctx, &node_secret); 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() @@ -854,12 +943,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[..]); @@ -871,6 +960,7 @@ impl KeysManager { let mut res = KeysManager { secp_ctx, node_secret, + node_id, inbound_payment_key: KeyMaterial(inbound_pmt_key_bytes), destination_script, @@ -910,7 +1000,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(); @@ -937,6 +1027,7 @@ impl KeysManager { InMemorySigner::new( &self.secp_ctx, + self.node_secret, funding_key, revocation_base_key, payment_key, @@ -953,7 +1044,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. @@ -971,8 +1062,8 @@ impl KeysManager { input.push(TxIn { previous_output: descriptor.outpoint.into_bitcoin_outpoint(), script_sig: Script::new(), - sequence: 0, - witness: Vec::new(), + sequence: Sequence::ZERO, + witness: Witness::new(), }); witness_weight += StaticPaymentOutputDescriptor::MAX_WITNESS_LENGTH; input_value += descriptor.output.value; @@ -982,8 +1073,8 @@ impl KeysManager { input.push(TxIn { previous_output: descriptor.outpoint.into_bitcoin_outpoint(), script_sig: Script::new(), - sequence: descriptor.to_self_delay as u32, - witness: Vec::new(), + sequence: Sequence(descriptor.to_self_delay as u32), + witness: Witness::new(), }); witness_weight += DelayedPaymentOutputDescriptor::MAX_WITNESS_LENGTH; input_value += descriptor.output.value; @@ -993,8 +1084,8 @@ impl KeysManager { input.push(TxIn { previous_output: outpoint.into_bitcoin_outpoint(), script_sig: Script::new(), - sequence: 0, - witness: Vec::new(), + sequence: Sequence::ZERO, + witness: Witness::new(), }); witness_weight += 1 + 73 + 34; input_value += output.value; @@ -1005,7 +1096,7 @@ impl KeysManager { } let mut spend_tx = Transaction { version: 2, - lock_time: 0, + lock_time: PackedLockTime(0), input, output: outputs, }; @@ -1022,7 +1113,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 { @@ -1030,7 +1121,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 { @@ -1050,29 +1141,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) } @@ -1081,8 +1173,26 @@ 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 get_node_id(&self, recipient: Recipient) -> Result { + match recipient { + Recipient::Node => Ok(self.node_id.clone()), + Recipient::PhantomNode => Err(()) + } + } + + fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result { + let mut node_secret = self.get_node_secret(recipient)?; + if let Some(tweak) = tweak { + node_secret = node_secret.mul_tweak(tweak).map_err(|_| ())?; + } + Ok(SharedSecret::new(other_key, &node_secret)) } fn get_inbound_payment_key_material(&self) -> KeyMaterial { @@ -1112,19 +1222,142 @@ 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)) + 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, + phantom_node_id: PublicKey, +} + +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 get_node_id(&self, recipient: Recipient) -> Result { + match recipient { + Recipient::Node => self.inner.get_node_id(Recipient::Node), + Recipient::PhantomNode => Ok(self.phantom_node_id.clone()), + } + } + + fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result { + let mut node_secret = self.get_node_secret(recipient)?; + if let Some(tweak) = tweak { + node_secret = node_secret.mul_tweak(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); + let phantom_secret = SecretKey::from_slice(&phantom_key).unwrap(); + let phantom_node_id = PublicKey::from_secret_key(&inner.secp_ctx, &phantom_secret); + Self { + inner, + inbound_payment_key: KeyMaterial(inbound_key), + phantom_secret, + phantom_node_id, + } + } + + /// 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) } }