X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fkeysinterface.rs;h=db1c6e943716c4a6c0cc2060e48f6646a5ef175c;hb=de2acc0ee0166e9d6cbd3a89459e08ee4ac6a4d5;hp=73b8a1b98224ace7aef2f6db05a82ca2a020e476;hpb=f99301dd8a7f056c0ed09ec827fddc00f325b3e5;p=rust-lightning diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index 73b8a1b9..db1c6e94 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -31,21 +31,22 @@ use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::RecoverableSignature; use bitcoin::{PackedLockTime, secp256k1, Sequence, 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; -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 prelude::*; +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 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 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. @@ -55,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, @@ -95,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, @@ -126,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 @@ -348,6 +349,12 @@ pub trait BaseSign { /// chosen to forgo their output as dust. fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1) -> Result; + /// 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. /// @@ -404,11 +411,28 @@ pub trait KeysInterface { /// /// 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. @@ -448,6 +472,8 @@ pub trait KeysInterface { /// The hrp is ascii bytes, while the invoice data is base32. /// /// 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. @@ -507,7 +533,8 @@ impl InMemorySigner { htlc_base_key: SecretKey, commitment_seed: [u8; 32], channel_value_satoshis: u64, - channel_keys_id: [u8; 32]) -> InMemorySigner { + channel_keys_id: [u8; 32], + ) -> InMemorySigner { let holder_channel_pubkeys = InMemorySigner::make_holder_keys(secp_ctx, &funding_key, &revocation_base_key, &payment_key, &delayed_payment_base_key, @@ -628,8 +655,7 @@ impl InMemorySigner { if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { 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_key = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &self.delayed_payment_base_key); 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!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]); @@ -645,6 +671,7 @@ impl InMemorySigner { witness.push(witness_script.clone().into_bytes()); Ok(witness) } + } impl BaseSign for InMemorySigner { @@ -677,11 +704,12 @@ impl BaseSign for InMemorySigner { let mut htlc_sigs = Vec::with_capacity(commitment_tx.htlcs().len()); 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 channel_parameters = self.get_channel_parameters(); + let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_tx.feerate_per_kw(), self.holder_selected_contest_delay(), htlc, self.opt_anchors(), channel_parameters.opt_non_zero_fee_anchors.is_some(), &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() { 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(|_| ())?; + let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key); htlc_sigs.push(sign(secp_ctx, &htlc_sighash, &holder_htlc_key)); } @@ -714,11 +742,11 @@ impl BaseSign for InMemorySigner { } fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1) -> Result { - let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key).map_err(|_| ())?; + let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key); let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key); - let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint).map_err(|_| ())?; + let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint); let witness_script = { - let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint).map_err(|_| ())?; + let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint); chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.holder_selected_contest_delay(), &counterparty_delayedpubkey) }; let mut sighash_parts = sighash::SighashCache::new(justice_tx); @@ -727,12 +755,12 @@ impl BaseSign for InMemorySigner { } fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1) -> Result { - let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key).map_err(|_| ())?; + let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key); let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key); - let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint).map_err(|_| ())?; + let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint); let witness_script = { - let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint).map_err(|_| ())?; - let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint).map_err(|_| ())?; + let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint); + let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint); chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey) }; let mut sighash_parts = sighash::SighashCache::new(justice_tx); @@ -741,19 +769,14 @@ impl BaseSign for InMemorySigner { } fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1) -> Result { - if let Ok(htlc_key) = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key) { - let witness_script = if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint) { - if let Ok(counterparty_htlcpubkey) = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint) { - if let Ok(htlcpubkey) = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint) { - chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey) - } else { return Err(()) } - } else { return Err(()) } - } else { return Err(()) }; - 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(()) + let htlc_key = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key); + let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint); + let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint); + let htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint); + let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.opt_anchors(), &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey); + 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()[..]); + Ok(sign(secp_ctx, &sighash, &htlc_key)) } fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1) -> Result { @@ -762,6 +785,16 @@ impl BaseSign for InMemorySigner { Ok(closing_tx.trust().sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx)) } + 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()[..])[..]); @@ -853,6 +886,7 @@ impl ReadableArgs for InMemorySigner { pub struct KeysManager { secp_ctx: Secp256k1, node_secret: SecretKey, + node_id: PublicKey, inbound_payment_key: KeyMaterial, destination_script: Script, shutdown_pubkey: PublicKey, @@ -894,6 +928,7 @@ impl KeysManager { 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; + 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_priv(&secp_ctx, &destination_key).to_pub().to_bytes()); @@ -921,6 +956,7 @@ impl KeysManager { let mut res = KeysManager { secp_ctx, node_secret, + node_id, inbound_payment_key: KeyMaterial(inbound_pmt_key_bytes), destination_script, @@ -995,7 +1031,7 @@ impl KeysManager { htlc_base_key, commitment_seed, channel_value_satoshis, - params.clone() + params.clone(), ) } @@ -1140,6 +1176,13 @@ impl KeysInterface for KeysManager { } } + 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 { @@ -1220,6 +1263,7 @@ pub struct PhantomKeysManager { inner: KeysManager, inbound_payment_key: KeyMaterial, phantom_secret: SecretKey, + phantom_node_id: PublicKey, } impl KeysInterface for PhantomKeysManager { @@ -1232,6 +1276,13 @@ impl KeysInterface for PhantomKeysManager { } } + 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 { @@ -1285,10 +1336,13 @@ impl 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: SecretKey::from_slice(&phantom_key).unwrap(), + phantom_secret, + phantom_node_id, } }