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;
use bitcoin::secp256k1;
use util::{byte_utils, transaction_utils};
-use util::ser::{Writeable, Writer, Readable};
+use util::crypto::hkdf_extract_expand_twice;
+use util::ser::{Writeable, Writer, Readable, ReadableArgs};
use chain::transaction::OutPoint;
-use ln::chan_utils;
-use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction};
+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 std::collections::HashSet;
-use std::sync::atomic::{AtomicUsize, Ordering};
-use std::io::Error;
+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.
pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH + 1;
}
+impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, {
+ (0, outpoint, required),
+ (2, per_commitment_point, required),
+ (4, to_self_delay, required),
+ (6, output, required),
+ (8, revocation_pubkey, required),
+ (10, channel_keys_id, required),
+ (12, channel_value_satoshis, required),
+});
+
/// Information about a spendable output to our "payment key". See
/// SpendableOutputDescriptor::StaticPaymentOutput for more details on how to spend this.
#[derive(Clone, Debug, PartialEq)]
// redeemscript push length.
pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 34;
}
+impl_writeable_tlv_based!(StaticPaymentOutputDescriptor, {
+ (0, outpoint, required),
+ (2, output, required),
+ (4, channel_keys_id, required),
+ (6, channel_value_satoshis, required),
+});
/// When on-chain outputs are created by rust-lightning (which our counterparty is not able to
/// claim at any point in the future) an event is generated which you must track and be able to
#[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 {
StaticPaymentOutput(StaticPaymentOutputDescriptor),
}
-impl Writeable for SpendableOutputDescriptor {
- fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
- match self {
- &SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
- 0u8.write(writer)?;
- outpoint.write(writer)?;
- output.write(writer)?;
- },
- &SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor) => {
- 1u8.write(writer)?;
- descriptor.outpoint.write(writer)?;
- descriptor.per_commitment_point.write(writer)?;
- descriptor.to_self_delay.write(writer)?;
- descriptor.output.write(writer)?;
- descriptor.revocation_pubkey.write(writer)?;
- descriptor.channel_keys_id.write(writer)?;
- descriptor.channel_value_satoshis.write(writer)?;
- },
- &SpendableOutputDescriptor::StaticPaymentOutput(ref descriptor) => {
- 2u8.write(writer)?;
- descriptor.outpoint.write(writer)?;
- descriptor.output.write(writer)?;
- descriptor.channel_keys_id.write(writer)?;
- descriptor.channel_value_satoshis.write(writer)?;
- },
- }
- Ok(())
- }
-}
-
-impl Readable for SpendableOutputDescriptor {
- fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
- match Readable::read(reader)? {
- 0u8 => Ok(SpendableOutputDescriptor::StaticOutput {
- outpoint: Readable::read(reader)?,
- output: Readable::read(reader)?,
- }),
- 1u8 => Ok(SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
- outpoint: Readable::read(reader)?,
- per_commitment_point: Readable::read(reader)?,
- to_self_delay: Readable::read(reader)?,
- output: Readable::read(reader)?,
- revocation_pubkey: Readable::read(reader)?,
- channel_keys_id: Readable::read(reader)?,
- channel_value_satoshis: Readable::read(reader)?,
- })),
- 2u8 => Ok(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
- outpoint: Readable::read(reader)?,
- output: Readable::read(reader)?,
- channel_keys_id: Readable::read(reader)?,
- channel_value_satoshis: Readable::read(reader)?,
- })),
- _ => Err(DecodeError::InvalidValue),
- }
- }
-}
+impl_writeable_tlv_based_enum!(SpendableOutputDescriptor,
+ (0, StaticOutput) => {
+ (0, outpoint, required),
+ (2, output, required),
+ },
+;
+ (1, DelayedPaymentOutput),
+ (2, StaticPaymentOutput),
+);
/// A trait to sign lightning channel transactions as described in BOLT 3.
///
/// 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.
+ ///
+ /// The preimages of outgoing HTLCs that were fulfilled since the last commitment are provided.
+ /// A validating signer should ensure that an HTLC output is removed only when the matching
+ /// preimage is provided, or when the value to holder is restored.
+ ///
+ /// NOTE: all the relevant preimages will be provided, but there may also be additional
+ /// irrelevant or duplicate preimages.
+ fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction, preimages: Vec<PaymentPreimage>) -> 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
/// 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.
+ ///
+ /// The preimages of outgoing HTLCs that were fulfilled since the last commitment are provided.
+ /// A validating signer should ensure that an HTLC output is removed only when the matching
+ /// preimage is provided, or when the value to holder is restored.
+ ///
+ /// NOTE: all the relevant preimages will be provided, but there may also be additional
+ /// irrelevant or duplicate preimages.
//
// TODO: Document the things someone using this interface should enforce before signing.
- fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
+ fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
+ /// 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
///
/// 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<secp256k1::All>) -> Result<Signature, ()>;
+ fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
- /// Signs a channel announcement message with our funding key, proving it comes from one
- /// of the channel participants.
+ /// 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<secp256k1::All>) -> Result<Signature, ()>;
+ fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>)
+ -> Result<(Signature, Signature), ()>;
/// Set the counterparty static channel data, including basepoints,
/// counterparty_selected/holder_selected_contest_delay and funding outpoint.
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 (aka node_id or network_key) based on the provided [`Recipient`].
///
- /// This method must return the same value each time it is called.
- fn get_node_secret(&self) -> SecretKey;
+ /// 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<SecretKey, ()>;
/// 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
/// 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!
///
/// you've read all of the provided bytes to ensure no corruption occurred.
fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError>;
- /// 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<u8>) -> Result<RecoverableSignature, ()>;
+ /// The hrp is ascii bytes, while the invoice data is base32.
+ ///
+ /// 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<RecoverableSignature, ()>;
+
+ /// 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;
}
#[derive(Clone)]
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<ChannelTransactionParameters>,
/// The total value of this channel
/// Create a new InMemorySigner
pub fn new<C: Signing>(
secp_ctx: &Secp256k1<C>,
+ node_secret: SecretKey,
funding_key: SecretKey,
revocation_base_key: SecretKey,
payment_key: SecretKey,
delayed_payment_base_key,
htlc_base_key,
commitment_seed,
+ node_secret,
channel_value_satoshis,
holder_channel_pubkeys,
channel_parameters: None,
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<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &StaticPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
// 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
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());
/// 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<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &DelayedPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
// 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
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());
chan_utils::build_commitment_secret(&self.commitment_seed, idx)
}
+ fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction, _preimages: Vec<PaymentPreimage>) -> Result<(), ()> {
+ Ok(())
+ }
+
fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys }
fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }
- fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
+ fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
let trusted_tx = commitment_tx.trust();
let keys = trusted_tx.keys();
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 holder_htlc_key = match chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key) {
- Ok(s) => s,
- Err(_) => return Err(()),
- };
+ 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));
}
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<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
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);
}
fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
- let revocation_key = match chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key) {
- Ok(revocation_key) => revocation_key,
- Err(_) => return Err(())
- };
+ let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key).map_err(|_| ())?;
let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
- let revocation_pubkey = match chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint) {
- Ok(revocation_pubkey) => revocation_pubkey,
- Err(_) => return Err(())
- };
+ let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint).map_err(|_| ())?;
let witness_script = {
- let counterparty_delayedpubkey = match chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint) {
- Ok(counterparty_delayedpubkey) => counterparty_delayedpubkey,
- Err(_) => return Err(())
- };
+ 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);
}
fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
- let revocation_key = match chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key) {
- Ok(revocation_key) => revocation_key,
- Err(_) => return Err(())
- };
+ let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key).map_err(|_| ())?;
let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
- let revocation_pubkey = match chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint) {
- Ok(revocation_pubkey) => revocation_pubkey,
- Err(_) => return Err(())
- };
+ let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint).map_err(|_| ())?;
let witness_script = {
- let counterparty_htlcpubkey = match chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint) {
- Ok(counterparty_htlcpubkey) => counterparty_htlcpubkey,
- Err(_) => return Err(())
- };
- let holder_htlcpubkey = match chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint) {
- Ok(holder_htlcpubkey) => holder_htlcpubkey,
- Err(_) => return Err(())
- };
- chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey)
+ 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, 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)[..]);
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(()) };
Err(())
}
- fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
- 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<secp256k1::All>) -> Result<Signature, ()> {
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<secp256k1::All>) -> Result<Signature, ()> {
+ fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>)
+ -> Result<(Signature, Signature), ()> {
let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]);
- Ok(secp_ctx.sign(&msghash, &self.funding_key))
+ Ok((secp_ctx.sign(&msghash, &self.node_secret), secp_ctx.sign(&msghash, &self.funding_key)))
}
fn ready_channel(&mut self, channel_parameters: &ChannelTransactionParameters) {
}
}
+const SERIALIZATION_VERSION: u8 = 1;
+const MIN_SERIALIZATION_VERSION: u8 = 1;
+
impl Sign for InMemorySigner {}
impl Writeable for InMemorySigner {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
+ write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
+
self.funding_key.write(writer)?;
self.revocation_base_key.write(writer)?;
self.payment_key.write(writer)?;
self.channel_value_satoshis.write(writer)?;
self.channel_keys_id.write(writer)?;
+ write_tlv_fields!(writer, {});
+
Ok(())
}
}
-impl Readable for InMemorySigner {
- fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
+impl ReadableArgs<SecretKey> for InMemorySigner {
+ fn read<R: io::Read>(reader: &mut R, node_secret: SecretKey) -> Result<Self, DecodeError> {
+ let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
+
let funding_key = Readable::read(reader)?;
let revocation_base_key = Readable::read(reader)?;
let payment_key = Readable::read(reader)?;
&htlc_base_key);
let keys_id = Readable::read(reader)?;
+ read_tlv_fields!(reader, {});
+
Ok(InMemorySigner {
funding_key,
revocation_base_key,
payment_key,
delayed_payment_base_key,
htlc_base_key,
+ node_secret,
commitment_seed,
channel_value_satoshis,
holder_channel_pubkeys,
/// 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<secp256k1::All>,
node_secret: SecretKey,
+ inbound_payment_key: KeyMaterial,
destination_script: Script,
shutdown_pubkey: PublicKey,
channel_master_key: ExtendedPrivKey,
};
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));
let mut res = KeysManager {
secp_ctx,
node_secret,
+ inbound_payment_key: KeyMaterial(inbound_pmt_key_bytes),
destination_script,
shutdown_pubkey,
/// onchain output detection for which a corresponding delayed_payment_key must be derived.
pub fn derive_channel_keys(&self, channel_value_satoshis: u64, params: &[u8; 32]) -> InMemorySigner {
let chan_id = byte_utils::slice_to_be64(¶ms[0..8]);
- assert!(chan_id <= std::u32::MAX as u64); // Otherwise the params field wasn't created by us
+ assert!(chan_id <= core::u32::MAX as u64); // Otherwise the params field wasn't created by us
let mut unique_start = Sha256::engine();
unique_start.input(params);
unique_start.input(&self.seed);
InMemorySigner::new(
&self.secp_ctx,
+ self.node_secret,
funding_key,
revocation_base_key,
payment_key,
/// 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.
///
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;
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 {
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 {
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());
}
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)
}
}
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<SecretKey, ()> {
+ match recipient {
+ Recipient::Node => Ok(self.node_secret.clone()),
+ Recipient::PhantomNode => Err(())
+ }
+ }
+
+ 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 {
let child_ix = self.channel_child_index.fetch_add(1, Ordering::AcqRel);
- assert!(child_ix <= std::u32::MAX as usize);
+ assert!(child_ix <= core::u32::MAX as usize);
let mut id = [0; 32];
id[0..8].copy_from_slice(&byte_utils::be64_to_array(child_ix as u64));
id[8..16].copy_from_slice(&byte_utils::be64_to_array(self.starting_time_nanos as u64));
}
fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
- InMemorySigner::read(&mut std::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<RecoverableSignature, ()> {
+ 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_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<SecretKey, ()> {
+ match recipient {
+ Recipient::Node => self.inner.get_node_secret(Recipient::Node),
+ Recipient::PhantomNode => Ok(self.phantom_secret.clone()),
+ }
+ }
+
+ fn get_inbound_payment_key_material(&self) -> KeyMaterial {
+ self.inbound_payment_key.clone()
}
- fn sign_invoice(&self, invoice_preimage: Vec<u8>) -> Result<RecoverableSignature, ()> {
- Ok(self.secp_ctx.sign_recoverable(&hash_to_message!(&Sha256::hash(&invoice_preimage)), &self.get_node_secret()))
+ 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::Signer, DecodeError> {
+ self.inner.read_chan_signer(reader)
+ }
+
+ fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result<RecoverableSignature, ()> {
+ let preimage = construct_invoice_preimage(&hrp_bytes, &invoice_data);
+ let secret = self.get_node_secret(recipient)?;
+ Ok(self.inner.secp_ctx.sign_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<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: Script, feerate_sat_per_1000_weight: u32, secp_ctx: &Secp256k1<C>) -> Result<Transaction, ()> {
+ 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)
}
}