X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fkeysinterface.rs;h=7538d0a83033c03f2f92f17a0dab8ff5c9be1ec5;hb=b54fe5fcc761e7d50c4388d605f1fcbde5aec41a;hp=7356dad8e40d64c353a80efa77b89e01c2fed7b8;hpb=09e167019589dcfc5ee9675ad243b337659eafc7;p=rust-lightning diff --git a/lightning/src/chain/keysinterface.rs b/lightning/src/chain/keysinterface.rs index 7356dad8..7538d0a8 100644 --- a/lightning/src/chain/keysinterface.rs +++ b/lightning/src/chain/keysinterface.rs @@ -18,6 +18,7 @@ use bitcoin::network::constants::Network; use bitcoin::util::bip32::{ExtendedPrivKey, ExtendedPubKey, ChildNumber}; use bitcoin::util::bip143; +use bitcoin::bech32::u5; use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::hashes::sha256::HashEngine as Sha256State; use bitcoin::hashes::sha256::Hash as Sha256; @@ -34,13 +35,21 @@ use util::ser::{Writeable, Writer, Readable}; use chain::transaction::OutPoint; use ln::chan_utils; -use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction}; +use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction}; use ln::msgs::UnsignedChannelAnnouncement; +use ln::script::ShutdownScript; use prelude::*; use core::sync::atomic::{AtomicUsize, Ordering}; use io::{self, Error}; use ln::msgs::{DecodeError, MAX_VALUE_MSAT}; +use 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. +/// (C-not exported) as we just use [u8; 32] directly +#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)] +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. @@ -118,8 +127,8 @@ impl_writeable_tlv_based!(StaticPaymentOutputDescriptor, { #[derive(Clone, Debug, PartialEq)] pub enum SpendableOutputDescriptor { /// An output to a script which was provided via KeysInterface directly, either from - /// `get_destination_script()` or `get_shutdown_pubkey()`, thus you should already know how to - /// spend it. No secret keys are provided as rust-lightning was never given any key. + /// `get_destination_script()` or `get_shutdown_scriptpubkey()`, thus you should already know + /// how to spend it. No secret keys are provided as rust-lightning was never given any key. /// These may include outputs from a transaction punishing our counterparty or claiming an HTLC /// on-chain using the payment preimage or after it has timed out. StaticOutput { @@ -211,6 +220,13 @@ pub trait BaseSign { /// Note that the commitment number starts at (1 << 48) - 1 and counts backwards. // TODO: return a Result so we can signal a validation error fn release_commitment_secret(&self, idx: u64) -> [u8; 32]; + /// Validate the counterparty's signatures on the holder commitment transaction and HTLCs. + /// + /// This is required in order for the signer to make sure that releasing a commitment + /// secret won't leave us without a broadcastable holder transaction. + /// Policy checks should be implemented in this function, including checking the amount + /// sent to us and checking the HTLCs. + fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction) -> Result<(), ()>; /// Gets the holder's channel public keys and basepoints fn pubkeys(&self) -> &ChannelPublicKeys; /// Gets an arbitrary identifier describing the set of keys which are provided back to you in @@ -221,9 +237,17 @@ pub trait BaseSign { /// Create a signature for a counterparty's commitment transaction and associated HTLC transactions. /// /// Note that if signing fails or is rejected, the channel will be force-closed. + /// + /// Policy checks should be implemented in this function, including checking the amount + /// sent to us and checking the HTLCs. // // TODO: Document the things someone using this interface should enforce before signing. fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()>; + /// Validate the counterparty's revocation. + /// + /// This is required in order for the signer to make sure that the state has moved + /// forward and it is safe to sign the next counterparty commitment. + fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()>; /// Create a signatures for a holder's commitment transaction and its claiming HTLC transactions. /// This will only ever be called with a non-revoked commitment_tx. This will be called with the @@ -306,7 +330,7 @@ pub trait BaseSign { /// /// Note that, due to rounding, there may be one "missing" satoshi, and either party may have /// chosen to forgo their output as dust. - fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1) -> Result; + 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. @@ -351,12 +375,11 @@ pub trait KeysInterface { /// This method should return a different value each time it is called, to avoid linking /// on-chain funds across channels as controlled to the same user. fn get_destination_script(&self) -> Script; - /// Get a public key which we will send funds to (in the form of a P2WPKH output) when closing - /// a channel. + /// Get a script pubkey which we will send funds to when closing a channel. /// /// This method should return a different value each time it is called, to avoid linking /// on-chain funds across channels as controlled to the same user. - fn get_shutdown_pubkey(&self) -> PublicKey; + fn get_shutdown_scriptpubkey(&self) -> ShutdownScript; /// Get a new set of Sign for per-channel secrets. These MUST be unique even if you /// restarted with some stale data! /// @@ -377,11 +400,17 @@ pub trait KeysInterface { /// you've read all of the provided bytes to ensure no corruption occurred. fn read_chan_signer(&self, reader: &[u8]) -> Result; - /// Sign an invoice's preimage (note that this is the preimage of the invoice, not the HTLC's - /// preimage). By parameterizing by the preimage instead of the hash, we allow implementors of + /// Sign an invoice. + /// By parameterizing by the raw invoice bytes instead of the hash, we allow implementors of /// this trait to parse the invoice and make sure they're signing what they expect, rather than /// blindly signing the hash. - fn sign_invoice(&self, invoice_preimage: Vec) -> Result; + /// The hrp is ascii bytes, while the invoice data is base32. + fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5]) -> Result; + + /// Get secret key material as bytes for use in encrypting and decrypting inbound payment data. + /// + /// This method must return the same value each time it is called. + fn get_inbound_payment_key_material(&self) -> KeyMaterial; } #[derive(Clone)] @@ -490,11 +519,18 @@ impl InMemorySigner { self.channel_parameters.as_ref().unwrap() } + /// Whether anchors should be used. + /// Will panic if ready_channel wasn't called. + pub fn opt_anchors(&self) -> bool { + self.get_channel_parameters().opt_anchors.is_some() + } + /// Sign the single input of spend_tx at index `input_idx` which spends the output /// described by descriptor, returning the witness stack for the input. /// /// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig, - /// or is not spending the outpoint described by `descriptor.outpoint`. + /// is not spending the outpoint described by `descriptor.outpoint`, + /// or if an output descriptor script_pubkey does not match the one we can spend. pub fn sign_counterparty_payment_input(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &StaticPaymentOutputDescriptor, secp_ctx: &Secp256k1) -> Result>, ()> { // TODO: We really should be taking the SigHashCache as a parameter here instead of // spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only @@ -508,6 +544,9 @@ impl InMemorySigner { 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(); + + if payment_script != descriptor.output.script_pubkey { return Err(()); } let mut witness = Vec::with_capacity(2); witness.push(remotesig.serialize_der().to_vec()); @@ -520,8 +559,9 @@ impl InMemorySigner { /// described by descriptor, returning the witness stack for the input. /// /// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig, - /// is not spending the outpoint described by `descriptor.outpoint`, or does not have a - /// sequence set to `descriptor.to_self_delay`. + /// is not spending the outpoint described by `descriptor.outpoint`, does not have a + /// sequence set to `descriptor.to_self_delay`, or if an output descriptor + /// script_pubkey does not match the one we can spend. pub fn sign_dynamic_p2wsh_input(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &DelayedPaymentOutputDescriptor, secp_ctx: &Secp256k1) -> Result>, ()> { // TODO: We really should be taking the SigHashCache as a parameter here instead of // spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only @@ -538,6 +578,9 @@ impl InMemorySigner { 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 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()); @@ -558,6 +601,10 @@ impl BaseSign for InMemorySigner { chan_utils::build_commitment_secret(&self.commitment_seed, idx) } + fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction) -> Result<(), ()> { + Ok(()) + } + fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys } fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id } @@ -574,9 +621,10 @@ 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, &keys.broadcaster_delayed_payment_key, &keys.revocation_key); - let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &keys); - let htlc_sighash = hash_to_message!(&bip143::SigHashCache::new(&htlc_tx).signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, SigHashType::All)[..]); + 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 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)); } @@ -584,6 +632,10 @@ impl BaseSign for InMemorySigner { Ok((commitment_sig, htlc_sigs)) } + fn validate_counterparty_revocation(&self, _idx: u64, _secret: &SecretKey) -> Result<(), ()> { + Ok(()) + } + fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key); let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey); @@ -625,7 +677,7 @@ impl BaseSign for InMemorySigner { 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(|_| ())?; - chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey) + 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)[..]); @@ -637,7 +689,7 @@ impl BaseSign for InMemorySigner { 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, &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey) + 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(()) }; @@ -648,17 +700,10 @@ impl BaseSign for InMemorySigner { Err(()) } - fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1) -> Result { - if closing_tx.input.len() != 1 { return Err(()); } - if closing_tx.input[0].witness.len() != 0 { return Err(()); } - if closing_tx.output.len() > 2 { return Err(()); } - + fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1) -> Result { let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key); let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey); - - let sighash = hash_to_message!(&bip143::SigHashCache::new(closing_tx) - .signature_hash(0, &channel_funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]); - Ok(secp_ctx.sign(&sighash, &self.funding_key)) + 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 { @@ -744,6 +789,7 @@ impl Readable for InMemorySigner { pub struct KeysManager { secp_ctx: Secp256k1, node_secret: SecretKey, + inbound_payment_key: KeyMaterial, destination_script: Script, shutdown_pubkey: PublicKey, channel_master_key: ExtendedPrivKey, @@ -799,6 +845,9 @@ impl KeysManager { }; 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 mut inbound_pmt_key_bytes = [0; 32]; + inbound_pmt_key_bytes.copy_from_slice(&inbound_payment_key[..]); let mut rand_bytes_unique_start = Sha256::engine(); rand_bytes_unique_start.input(&byte_utils::be64_to_array(starting_time_secs)); @@ -808,6 +857,7 @@ impl KeysManager { let mut res = KeysManager { secp_ctx, node_secret, + inbound_payment_key: KeyMaterial(inbound_pmt_key_bytes), destination_script, shutdown_pubkey, @@ -888,8 +938,9 @@ impl KeysManager { /// output to the given change destination (if sufficient change value remains). The /// 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 or - /// if a descriptor was duplicated. + /// 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 + /// 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. /// @@ -944,7 +995,8 @@ impl KeysManager { input, output: outputs, }; - transaction_utils::maybe_add_change_output(&mut spend_tx, input_value, witness_weight, feerate_sat_per_1000_weight, change_destination_script)?; + let expected_max_weight = + transaction_utils::maybe_add_change_output(&mut spend_tx, input_value, witness_weight, feerate_sat_per_1000_weight, change_destination_script)?; let mut keys_cache: Option<(InMemorySigner, [u8; 32])> = None; let mut input_idx = 0; @@ -956,7 +1008,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).unwrap(); + spend_tx.input[input_idx].witness = 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 { @@ -964,7 +1016,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).unwrap(); + spend_tx.input[input_idx].witness = 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 { @@ -989,6 +1041,10 @@ impl KeysManager { assert_eq!(pubkey.key, 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()); @@ -998,6 +1054,12 @@ impl KeysManager { } input_idx += 1; } + + debug_assert!(expected_max_weight >= spend_tx.get_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); + Ok(spend_tx) } } @@ -1009,12 +1071,16 @@ impl KeysInterface for KeysManager { self.node_secret.clone() } + fn get_inbound_payment_key_material(&self) -> KeyMaterial { + self.inbound_payment_key.clone() + } + fn get_destination_script(&self) -> Script { self.destination_script.clone() } - fn get_shutdown_pubkey(&self) -> PublicKey { - self.shutdown_pubkey.clone() + fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { + ShutdownScript::new_p2wpkh_from_pubkey(self.shutdown_pubkey.clone()) } fn get_channel_signer(&self, _inbound: bool, channel_value_satoshis: u64) -> Self::Signer { @@ -1042,8 +1108,9 @@ impl KeysInterface for KeysManager { InMemorySigner::read(&mut io::Cursor::new(reader)) } - fn sign_invoice(&self, invoice_preimage: Vec) -> Result { - Ok(self.secp_ctx.sign_recoverable(&hash_to_message!(&Sha256::hash(&invoice_preimage)), &self.get_node_secret())) + fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5]) -> 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())) } }