Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 149 additions & 19 deletions lightning/src/chain/keysinterface.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,12 +33,13 @@ use util::ser::{Writeable, Writer, Readable};

use chain::transaction::OutPoint;
use ln::chan_utils;
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys};
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys, derive_public_key, derive_public_revocation_key, TxCreationKeys};
use ln::msgs::UnsignedChannelAnnouncement;

use std::sync::atomic::{AtomicUsize, Ordering};
use std::io::Error;
use ln::msgs::DecodeError;
use ln::transaction::{CommitmentInfo, build_commitment_tx, get_commitment_transaction_number_obscure_factor};

/// 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
Expand DownExpand Up@@ -79,7 +80,7 @@ pub enum SpendableOutputDescriptor {
///
/// To derive the revocation_pubkey provided here (which is used in the witness
/// script generation), you must pass the counterparty revocation_basepoint (which appears in the
/// call to ChannelKeys::on_accept) and the provided per_commitment point
/// call to ChannelKeys::ready_channel) and the provided per_commitment point
/// to chan_utils::derive_public_revocation_key.
///
/// The witness script which is hashed and included in the output script_pubkey may be
Expand DownExpand Up@@ -244,6 +245,18 @@ pub trait ChannelKeys : Send+Clone {
// TODO: Add more input vars to enable better checking (preferably removing commitment_tx and
fn sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Create a signature for a holder's commitment transaction and attached HTLC transactions.
fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replaces the previous function.

&self,
commitment_number: u64,
feerate_per_kw: u32,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
pre_keys: &PreCalculatedTxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(Signature, Vec<Signature>), ()>;

/// Same as sign_holder_commitment, but exists only for tests to get access to holder commitment
/// transactions which will be broadcasted later, after the channel has moved on to a newer
/// state. Thus, needs its own method as sign_holder_commitment may enforce that we only ever
Expand DownExpand Up@@ -319,13 +332,15 @@ pub trait ChannelKeys : Send+Clone {
/// protocol.
fn sign_channel_announcement<T: secp256k1::Signing>(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Set the counterparty channel basepoints and counterparty_selected/holder_selected_contest_delay.
/// This is done immediately on incoming channels and as soon as the channel is accepted on outgoing channels.
/// Set the counterparty static channel data, including basepoints,
/// counterparty_selected/holder_selected_contest_delay and funding outpoint.
/// This is done as soon as the funding outpoint is known. Since these are static channel data,
/// they MUST NOT be allowed to change to different values once set.
///
/// We bind holder_selected_contest_delay late here for API convenience.
///
/// Will be called before any signatures are applied.
fn on_accept(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16);
fn ready_channel(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint);
}

/// A trait to describe an object which can get user secrets and key material.
Expand All@@ -349,11 +364,11 @@ pub trait KeysInterface: Send + Sync {
}

#[derive(Clone)]
/// Holds late-bound channel data.
/// This data is available after the channel is known to be accepted, either
/// when receiving an open_channel for an inbound channel or when
/// receiving accept_channel for an outbound channel.
struct AcceptedChannelData {
/// Holds late-bound static channel data.
/// This data is available after the channel is readied, either
/// when receiving an funding_created for an inbound channel or when
/// creating a funding transaction for an outbound channel.
struct StaticChannelData {
/// Counterparty public keys and base points
counterparty_channel_pubkeys: ChannelPublicKeys,
/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
Expand All@@ -365,6 +380,8 @@ struct AcceptedChannelData {
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
holder_selected_contest_delay: u16,
/// Whether the holder is the initiator of this channel
Comment thread
devrandom marked this conversation as resolved.
is_outbound: bool,
}

#[derive(Clone)]
Expand All@@ -385,7 +402,9 @@ pub struct InMemoryChannelKeys {
/// Holder public keys and basepoints
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
/// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance
accepted_channel_data: Option<AcceptedChannelData>,
accepted_channel_data: Option<StaticChannelData>,
/// Funding outpoint, populated on channel funding creation
funding_outpoint: Option<OutPoint>,
/// The total value of this channel
channel_value_satoshis: u64,
/// Key derivation parameters
Expand DownExpand Up@@ -418,6 +437,7 @@ impl InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: None,
funding_outpoint: None,
key_derivation_params,
}
}
Expand All@@ -439,21 +459,83 @@ impl InMemoryChannelKeys {
}

/// Counterparty pubkeys.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.accepted_channel_data.as_ref().unwrap().counterparty_channel_pubkeys }

/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
/// transactions, ie the amount of time that we have to wait to recover our funds if we
/// broadcast a transaction. You'll likely want to pass this to the
/// ln::chan_utils::build*_transaction functions when signing holder's transactions.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().counterparty_selected_contest_delay }

/// The contest_delay value specified by us and applied on transactions broadcastable
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn holder_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().holder_selected_contest_delay }

/// Whether the holder is the initiator
pub fn is_outbound(&self) -> bool { self.accepted_channel_data.as_ref().unwrap().is_outbound }

/// Funding outpoint
/// Will panic if ready_channel wasn't called.
pub fn funding_outpoint(&self) -> &OutPoint { self.funding_outpoint.as_ref().unwrap() }

/// Prepare build information for a commitment tx that the holder can broadcast
pub fn build_holder_commitment_tx<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this need to be in the public interface, or are you imaging this is only a utility function in the InMemory version?

&self,
commitment_number: u64,
per_commitment_point: &PublicKey,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
keys: &TxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(bitcoin::Transaction, Vec<HTLCOutputInCommitment>, Vec<Script>), ()> {
let counterparty_points = self.counterparty_pubkeys();

let to_holder_delayed_pubkey = derive_public_key(
&secp_ctx,
&per_commitment_point,
&self.pubkeys().delayed_payment_basepoint,
).map_err(|_| ())?;

let revocation_pubkey = derive_public_revocation_key(
&secp_ctx,
&per_commitment_point,
&counterparty_points.revocation_basepoint,
).map_err(|_| ())?;

let info = CommitmentInfo {
is_counterparty_broadcaster: false,
to_countersigner_pubkey: counterparty_points.payment_point,
to_countersigner_value_sat: to_counterparty_value_sat,
revocation_pubkey,
to_broadcaster_delayed_pubkey: to_holder_delayed_pubkey,
to_broadcaster_value_sat: to_holder_value_sat,
to_self_delay: self.counterparty_selected_contest_delay(),
htlcs: htlcs.clone(),
};

let obscured_commitment_transaction_number =
self.get_commitment_transaction_number_obscure_factor() ^ commitment_number;
let (tx, htlcs, scripts) = build_commitment_tx(
&keys,
&info,
obscured_commitment_transaction_number,
self.funding_outpoint().into_bitcoin_outpoint(),
);
Ok((tx, htlcs, scripts))
}

fn get_commitment_transaction_number_obscure_factor(&self) -> u64 {
get_commitment_transaction_number_obscure_factor(
&self.pubkeys().payment_point,
&self.counterparty_pubkeys().payment_point,
self.is_outbound(),
)
}
}

impl ChannelKeys for InMemoryChannelKeys {
Expand DownExpand Up@@ -507,6 +589,48 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(holder_commitment_tx.get_holder_sig(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
}

fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_number: u64, feerate_per_kw: u32, to_holder_value_sat: u64, to_counterparty_value_sat: u64, htlcs: &Vec<HTLCOutputInCommitment>, pre_keys: &PreCalculatedTxCreationKeys, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()> {
let per_commitment_point = self.get_per_commitment_point(commitment_number, secp_ctx);
let keys = pre_keys.trust_key_derivation();
let (tx, htlcs, _) = self.build_holder_commitment_tx(
commitment_number,
&per_commitment_point,
to_holder_value_sat,
to_counterparty_value_sat,
htlcs,
keys,
secp_ctx,
)?;

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);
let sighash = hash_to_message!(&bip143::SigHashCache::new(&tx)
.signature_hash(0, &funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
let sig = secp_ctx.sign(&sighash, &self.funding_key);

// We provide a dummy signature for the remote, since we don't require that sig
// to be passed in to this call. It would have been better if HolderCommitmentTransaction
// didn't require the remote sig.
let dummy_sig = Secp256k1::new().sign(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This awkwardness is due to HolderCommitmentTransaction holding counterparty signatures. Not sure what's the best thing here - perhaps separate the signature holding to another struct?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would like to get ride of HolderCommitmentTransaction. It's API can be enforced either by ChannelMonitor or an external signer implementation like InMemoryChannelKeys.

All dynamic state (balances, HTLCs, ...) would be stored in Channel and the rest assumed to be in signer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why? HolderCommitmentTransaction, indeed, holds too much data today (specifically, the transaction itself), but communicating things like the counterparty signatures and the per htlc values has to be done. eg here the function signature has a ton of fields, that may be usefully just shoved into a struct, and HolderCommitmentTransaction is basically that. (I assume we agree here, just noting that we may end up wanting a struct either way).

&secp256k1::Message::from_slice(&[42; 32]).unwrap(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
let htlcs_with_sig = htlcs.iter().map(|h| (h.clone(), Some(dummy_sig.clone()))).collect();
let commitment_tx = HolderCommitmentTransaction::new_missing_holder_sig(
tx,
dummy_sig,
&self.pubkeys().funding_pubkey,
&self.counterparty_pubkeys().funding_pubkey,
keys.clone(),
feerate_per_kw,
htlcs_with_sig,
);
let htlc_sigs_o = self.sign_holder_commitment_htlc_transactions(&commitment_tx, secp_ctx)?;
let htlc_sigs = htlc_sigs_o.iter().map(|o| o.unwrap()).collect();

Ok((sig, htlc_sigs))
}

#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
fn unsafe_sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
Expand DownExpand Up@@ -588,18 +712,21 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(secp_ctx.sign(&msghash, &self.funding_key))
}

fn on_accept(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16) {
assert!(self.accepted_channel_data.is_none(), "Already accepted");
self.accepted_channel_data = Some(AcceptedChannelData {
fn ready_channel(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint) {
assert!(self.accepted_channel_data.is_none(), "Acceptance already noted");
self.accepted_channel_data = Some(StaticChannelData {
counterparty_channel_pubkeys: channel_pubkeys.clone(),
counterparty_selected_contest_delay,
holder_selected_contest_delay,
is_outbound,
});
assert!(self.funding_outpoint.is_none(), "Funding creation already noted");
self.funding_outpoint = Some(funding_outpoint.clone());
}
}

impl_writeable!(AcceptedChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay });
impl_writeable!(StaticChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay, is_outbound });

impl Writeable for InMemoryChannelKeys {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
Expand All@@ -610,6 +737,7 @@ impl Writeable for InMemoryChannelKeys {
self.htlc_base_key.write(writer)?;
self.commitment_seed.write(writer)?;
self.accepted_channel_data.write(writer)?;
self.funding_outpoint.write(writer)?;
self.channel_value_satoshis.write(writer)?;
self.key_derivation_params.0.write(writer)?;
self.key_derivation_params.1.write(writer)?;
Expand All@@ -627,6 +755,7 @@ impl Readable for InMemoryChannelKeys {
let htlc_base_key = Readable::read(reader)?;
let commitment_seed = Readable::read(reader)?;
let counterparty_channel_data = Readable::read(reader)?;
let funding_outpoint = Readable::read(reader)?;
let channel_value_satoshis = Readable::read(reader)?;
let secp_ctx = Secp256k1::signing_only();
let holder_channel_pubkeys =
Expand All@@ -646,6 +775,7 @@ impl Readable for InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: counterparty_channel_data,
funding_outpoint,
key_derivation_params: (params_1, params_2),
})
}
Expand Down
16 changes: 15 additions & 1 deletion lightning/src/ln/chan_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -372,7 +372,8 @@ impl_writeable!(ChannelPublicKeys, 33*5, {


impl TxCreationKeys {
/// Create a new TxCreationKeys from channel base points and the per-commitment point
/// Create per-state keys from channel base points and the per-commitment point
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> Result<TxCreationKeys, SecpError> {
Ok(TxCreationKeys {
per_commitment_point: per_commitment_point.clone(),
Expand All@@ -382,6 +383,19 @@ impl TxCreationKeys {
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base)?,
})
}

/// Generate per-state keys from channel static keys.
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn make_tx_keys<T: secp256k1::Signing + secp256k1::Verification>(per_commitment_point: &PublicKey, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TxCreationKeys, SecpError> {
Comment thread
devrandom marked this conversation as resolved.
TxCreationKeys::derive_new(
&secp_ctx,
&per_commitment_point,
&broadcaster_keys.delayed_payment_basepoint,
&broadcaster_keys.htlc_basepoint,
&countersignatory_keys.revocation_basepoint,
&countersignatory_keys.htlc_basepoint,
)
}
}

/// A script either spendable by the revocation
Expand Down
10 changes: 5 additions & 5 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -582,15 +582,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
where K::Target: KeysInterface<ChanKeySigner = ChanSigner>,
F::Target: FeeEstimator
{
let mut chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: msg.funding_pubkey,
revocation_basepoint: msg.revocation_basepoint,
payment_point: msg.payment_point,
delayed_payment_basepoint: msg.delayed_payment_basepoint,
htlc_basepoint: msg.htlc_basepoint
};
chan_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, config.own_channel_config.our_to_self_delay);
let mut local_config = (*config).channel_options.clone();

if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT {
Expand DownExpand Up@@ -1463,7 +1462,6 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
htlc_basepoint: msg.htlc_basepoint
};

self.holder_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, self.holder_selected_contest_delay);
self.counterparty_pubkeys = Some(counterparty_pubkeys);

self.counterparty_cur_commitment_point = Some(msg.first_per_commitment_point);
Expand DownExpand Up@@ -1518,6 +1516,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
}

let funding_txo = OutPoint{ txid: msg.funding_txid, index: msg.funding_output_index };
self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());

let (counterparty_initial_commitment_tx, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
Expand DownExpand Up@@ -3545,6 +3544,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
}

self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());
let signature = match self.get_outbound_funding_created_signature(logger) {
Ok(res) => res,
Expand DownExpand Up@@ -4647,7 +4647,7 @@ mod tests {
chan.holder_dust_limit_satoshis = 546;

let funding_info = OutPoint{ txid: Txid::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), index: 0 };
chan.funding_txo = Some(funding_info);
chan.funding_txo = Some(funding_info.clone());

let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
Expand All@@ -4656,7 +4656,7 @@ mod tests {
delayed_payment_basepoint: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
htlc_basepoint: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444")
};
chan_keys.on_accept(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay);
chan_keys.ready_channel(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay, false, &funding_info);

assert_eq!(counterparty_pubkeys.payment_point.serialize()[..],
hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]);
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ pub mod peer_handler;
pub mod chan_utils;
pub mod features;
pub(crate) mod onchaintx;
pub mod transaction;

#[cfg(feature = "fuzztarget")]
pub mod peer_channel_encryptor;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Draft: phase-2 signer API by devrandom · Pull Request #723 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 149 additions & 19 deletions lightning/src/chain/keysinterface.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,12 +33,13 @@ use util::ser::{Writeable, Writer, Readable};

use chain::transaction::OutPoint;
use ln::chan_utils;
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys};
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys, derive_public_key, derive_public_revocation_key, TxCreationKeys};
use ln::msgs::UnsignedChannelAnnouncement;

use std::sync::atomic::{AtomicUsize, Ordering};
use std::io::Error;
use ln::msgs::DecodeError;
use ln::transaction::{CommitmentInfo, build_commitment_tx, get_commitment_transaction_number_obscure_factor};

/// 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
Expand DownExpand Up@@ -79,7 +80,7 @@ pub enum SpendableOutputDescriptor {
///
/// To derive the revocation_pubkey provided here (which is used in the witness
/// script generation), you must pass the counterparty revocation_basepoint (which appears in the
/// call to ChannelKeys::on_accept) and the provided per_commitment point
/// call to ChannelKeys::ready_channel) and the provided per_commitment point
/// to chan_utils::derive_public_revocation_key.
///
/// The witness script which is hashed and included in the output script_pubkey may be
Expand DownExpand Up@@ -244,6 +245,18 @@ pub trait ChannelKeys : Send+Clone {
// TODO: Add more input vars to enable better checking (preferably removing commitment_tx and
fn sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Create a signature for a holder's commitment transaction and attached HTLC transactions.
fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replaces the previous function.

&self,
commitment_number: u64,
feerate_per_kw: u32,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
pre_keys: &PreCalculatedTxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(Signature, Vec<Signature>), ()>;

/// Same as sign_holder_commitment, but exists only for tests to get access to holder commitment
/// transactions which will be broadcasted later, after the channel has moved on to a newer
/// state. Thus, needs its own method as sign_holder_commitment may enforce that we only ever
Expand DownExpand Up@@ -319,13 +332,15 @@ pub trait ChannelKeys : Send+Clone {
/// protocol.
fn sign_channel_announcement<T: secp256k1::Signing>(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Set the counterparty channel basepoints and counterparty_selected/holder_selected_contest_delay.
/// This is done immediately on incoming channels and as soon as the channel is accepted on outgoing channels.
/// Set the counterparty static channel data, including basepoints,
/// counterparty_selected/holder_selected_contest_delay and funding outpoint.
/// This is done as soon as the funding outpoint is known. Since these are static channel data,
/// they MUST NOT be allowed to change to different values once set.
///
/// We bind holder_selected_contest_delay late here for API convenience.
///
/// Will be called before any signatures are applied.
fn on_accept(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16);
fn ready_channel(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint);
}

/// A trait to describe an object which can get user secrets and key material.
Expand All@@ -349,11 +364,11 @@ pub trait KeysInterface: Send + Sync {
}

#[derive(Clone)]
/// Holds late-bound channel data.
/// This data is available after the channel is known to be accepted, either
/// when receiving an open_channel for an inbound channel or when
/// receiving accept_channel for an outbound channel.
struct AcceptedChannelData {
/// Holds late-bound static channel data.
/// This data is available after the channel is readied, either
/// when receiving an funding_created for an inbound channel or when
/// creating a funding transaction for an outbound channel.
struct StaticChannelData {
/// Counterparty public keys and base points
counterparty_channel_pubkeys: ChannelPublicKeys,
/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
Expand All@@ -365,6 +380,8 @@ struct AcceptedChannelData {
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
holder_selected_contest_delay: u16,
/// Whether the holder is the initiator of this channel
Comment thread
devrandom marked this conversation as resolved.
is_outbound: bool,
}

#[derive(Clone)]
Expand All@@ -385,7 +402,9 @@ pub struct InMemoryChannelKeys {
/// Holder public keys and basepoints
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
/// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance
accepted_channel_data: Option<AcceptedChannelData>,
accepted_channel_data: Option<StaticChannelData>,
/// Funding outpoint, populated on channel funding creation
funding_outpoint: Option<OutPoint>,
/// The total value of this channel
channel_value_satoshis: u64,
/// Key derivation parameters
Expand DownExpand Up@@ -418,6 +437,7 @@ impl InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: None,
funding_outpoint: None,
key_derivation_params,
}
}
Expand All@@ -439,21 +459,83 @@ impl InMemoryChannelKeys {
}

/// Counterparty pubkeys.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.accepted_channel_data.as_ref().unwrap().counterparty_channel_pubkeys }

/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
/// transactions, ie the amount of time that we have to wait to recover our funds if we
/// broadcast a transaction. You'll likely want to pass this to the
/// ln::chan_utils::build*_transaction functions when signing holder's transactions.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().counterparty_selected_contest_delay }

/// The contest_delay value specified by us and applied on transactions broadcastable
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn holder_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().holder_selected_contest_delay }

/// Whether the holder is the initiator
pub fn is_outbound(&self) -> bool { self.accepted_channel_data.as_ref().unwrap().is_outbound }

/// Funding outpoint
/// Will panic if ready_channel wasn't called.
pub fn funding_outpoint(&self) -> &OutPoint { self.funding_outpoint.as_ref().unwrap() }

/// Prepare build information for a commitment tx that the holder can broadcast
pub fn build_holder_commitment_tx<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this need to be in the public interface, or are you imaging this is only a utility function in the InMemory version?

&self,
commitment_number: u64,
per_commitment_point: &PublicKey,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
keys: &TxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(bitcoin::Transaction, Vec<HTLCOutputInCommitment>, Vec<Script>), ()> {
let counterparty_points = self.counterparty_pubkeys();

let to_holder_delayed_pubkey = derive_public_key(
&secp_ctx,
&per_commitment_point,
&self.pubkeys().delayed_payment_basepoint,
).map_err(|_| ())?;

let revocation_pubkey = derive_public_revocation_key(
&secp_ctx,
&per_commitment_point,
&counterparty_points.revocation_basepoint,
).map_err(|_| ())?;

let info = CommitmentInfo {
is_counterparty_broadcaster: false,
to_countersigner_pubkey: counterparty_points.payment_point,
to_countersigner_value_sat: to_counterparty_value_sat,
revocation_pubkey,
to_broadcaster_delayed_pubkey: to_holder_delayed_pubkey,
to_broadcaster_value_sat: to_holder_value_sat,
to_self_delay: self.counterparty_selected_contest_delay(),
htlcs: htlcs.clone(),
};

let obscured_commitment_transaction_number =
self.get_commitment_transaction_number_obscure_factor() ^ commitment_number;
let (tx, htlcs, scripts) = build_commitment_tx(
&keys,
&info,
obscured_commitment_transaction_number,
self.funding_outpoint().into_bitcoin_outpoint(),
);
Ok((tx, htlcs, scripts))
}

fn get_commitment_transaction_number_obscure_factor(&self) -> u64 {
get_commitment_transaction_number_obscure_factor(
&self.pubkeys().payment_point,
&self.counterparty_pubkeys().payment_point,
self.is_outbound(),
)
}
}

impl ChannelKeys for InMemoryChannelKeys {
Expand DownExpand Up@@ -507,6 +589,48 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(holder_commitment_tx.get_holder_sig(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
}

fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_number: u64, feerate_per_kw: u32, to_holder_value_sat: u64, to_counterparty_value_sat: u64, htlcs: &Vec<HTLCOutputInCommitment>, pre_keys: &PreCalculatedTxCreationKeys, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()> {
let per_commitment_point = self.get_per_commitment_point(commitment_number, secp_ctx);
let keys = pre_keys.trust_key_derivation();
let (tx, htlcs, _) = self.build_holder_commitment_tx(
commitment_number,
&per_commitment_point,
to_holder_value_sat,
to_counterparty_value_sat,
htlcs,
keys,
secp_ctx,
)?;

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);
let sighash = hash_to_message!(&bip143::SigHashCache::new(&tx)
.signature_hash(0, &funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
let sig = secp_ctx.sign(&sighash, &self.funding_key);

// We provide a dummy signature for the remote, since we don't require that sig
// to be passed in to this call. It would have been better if HolderCommitmentTransaction
// didn't require the remote sig.
let dummy_sig = Secp256k1::new().sign(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This awkwardness is due to HolderCommitmentTransaction holding counterparty signatures. Not sure what's the best thing here - perhaps separate the signature holding to another struct?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would like to get ride of HolderCommitmentTransaction. It's API can be enforced either by ChannelMonitor or an external signer implementation like InMemoryChannelKeys.

All dynamic state (balances, HTLCs, ...) would be stored in Channel and the rest assumed to be in signer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why? HolderCommitmentTransaction, indeed, holds too much data today (specifically, the transaction itself), but communicating things like the counterparty signatures and the per htlc values has to be done. eg here the function signature has a ton of fields, that may be usefully just shoved into a struct, and HolderCommitmentTransaction is basically that. (I assume we agree here, just noting that we may end up wanting a struct either way).

&secp256k1::Message::from_slice(&[42; 32]).unwrap(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
let htlcs_with_sig = htlcs.iter().map(|h| (h.clone(), Some(dummy_sig.clone()))).collect();
let commitment_tx = HolderCommitmentTransaction::new_missing_holder_sig(
tx,
dummy_sig,
&self.pubkeys().funding_pubkey,
&self.counterparty_pubkeys().funding_pubkey,
keys.clone(),
feerate_per_kw,
htlcs_with_sig,
);
let htlc_sigs_o = self.sign_holder_commitment_htlc_transactions(&commitment_tx, secp_ctx)?;
let htlc_sigs = htlc_sigs_o.iter().map(|o| o.unwrap()).collect();

Ok((sig, htlc_sigs))
}

#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
fn unsafe_sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
Expand DownExpand Up@@ -588,18 +712,21 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(secp_ctx.sign(&msghash, &self.funding_key))
}

fn on_accept(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16) {
assert!(self.accepted_channel_data.is_none(), "Already accepted");
self.accepted_channel_data = Some(AcceptedChannelData {
fn ready_channel(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint) {
assert!(self.accepted_channel_data.is_none(), "Acceptance already noted");
self.accepted_channel_data = Some(StaticChannelData {
counterparty_channel_pubkeys: channel_pubkeys.clone(),
counterparty_selected_contest_delay,
holder_selected_contest_delay,
is_outbound,
});
assert!(self.funding_outpoint.is_none(), "Funding creation already noted");
self.funding_outpoint = Some(funding_outpoint.clone());
}
}

impl_writeable!(AcceptedChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay });
impl_writeable!(StaticChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay, is_outbound });

impl Writeable for InMemoryChannelKeys {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
Expand All@@ -610,6 +737,7 @@ impl Writeable for InMemoryChannelKeys {
self.htlc_base_key.write(writer)?;
self.commitment_seed.write(writer)?;
self.accepted_channel_data.write(writer)?;
self.funding_outpoint.write(writer)?;
self.channel_value_satoshis.write(writer)?;
self.key_derivation_params.0.write(writer)?;
self.key_derivation_params.1.write(writer)?;
Expand All@@ -627,6 +755,7 @@ impl Readable for InMemoryChannelKeys {
let htlc_base_key = Readable::read(reader)?;
let commitment_seed = Readable::read(reader)?;
let counterparty_channel_data = Readable::read(reader)?;
let funding_outpoint = Readable::read(reader)?;
let channel_value_satoshis = Readable::read(reader)?;
let secp_ctx = Secp256k1::signing_only();
let holder_channel_pubkeys =
Expand All@@ -646,6 +775,7 @@ impl Readable for InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: counterparty_channel_data,
funding_outpoint,
key_derivation_params: (params_1, params_2),
})
}
Expand Down
16 changes: 15 additions & 1 deletion lightning/src/ln/chan_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -372,7 +372,8 @@ impl_writeable!(ChannelPublicKeys, 33*5, {


impl TxCreationKeys {
/// Create a new TxCreationKeys from channel base points and the per-commitment point
/// Create per-state keys from channel base points and the per-commitment point
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> Result<TxCreationKeys, SecpError> {
Ok(TxCreationKeys {
per_commitment_point: per_commitment_point.clone(),
Expand All@@ -382,6 +383,19 @@ impl TxCreationKeys {
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base)?,
})
}

/// Generate per-state keys from channel static keys.
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn make_tx_keys<T: secp256k1::Signing + secp256k1::Verification>(per_commitment_point: &PublicKey, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TxCreationKeys, SecpError> {
Comment thread
devrandom marked this conversation as resolved.
TxCreationKeys::derive_new(
&secp_ctx,
&per_commitment_point,
&broadcaster_keys.delayed_payment_basepoint,
&broadcaster_keys.htlc_basepoint,
&countersignatory_keys.revocation_basepoint,
&countersignatory_keys.htlc_basepoint,
)
}
}

/// A script either spendable by the revocation
Expand Down
10 changes: 5 additions & 5 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -582,15 +582,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
where K::Target: KeysInterface<ChanKeySigner = ChanSigner>,
F::Target: FeeEstimator
{
let mut chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: msg.funding_pubkey,
revocation_basepoint: msg.revocation_basepoint,
payment_point: msg.payment_point,
delayed_payment_basepoint: msg.delayed_payment_basepoint,
htlc_basepoint: msg.htlc_basepoint
};
chan_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, config.own_channel_config.our_to_self_delay);
let mut local_config = (*config).channel_options.clone();

if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT {
Expand DownExpand Up@@ -1463,7 +1462,6 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
htlc_basepoint: msg.htlc_basepoint
};

self.holder_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, self.holder_selected_contest_delay);
self.counterparty_pubkeys = Some(counterparty_pubkeys);

self.counterparty_cur_commitment_point = Some(msg.first_per_commitment_point);
Expand DownExpand Up@@ -1518,6 +1516,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
}

let funding_txo = OutPoint{ txid: msg.funding_txid, index: msg.funding_output_index };
self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());

let (counterparty_initial_commitment_tx, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
Expand DownExpand Up@@ -3545,6 +3544,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
}

self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());
let signature = match self.get_outbound_funding_created_signature(logger) {
Ok(res) => res,
Expand DownExpand Up@@ -4647,7 +4647,7 @@ mod tests {
chan.holder_dust_limit_satoshis = 546;

let funding_info = OutPoint{ txid: Txid::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), index: 0 };
chan.funding_txo = Some(funding_info);
chan.funding_txo = Some(funding_info.clone());

let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
Expand All@@ -4656,7 +4656,7 @@ mod tests {
delayed_payment_basepoint: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
htlc_basepoint: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444")
};
chan_keys.on_accept(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay);
chan_keys.ready_channel(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay, false, &funding_info);

assert_eq!(counterparty_pubkeys.payment_point.serialize()[..],
hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]);
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ pub mod peer_handler;
pub mod chan_utils;
pub mod features;
pub(crate) mod onchaintx;
pub mod transaction;

#[cfg(feature = "fuzztarget")]
pub mod peer_channel_encryptor;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Draft: phase-2 signer API by devrandom · Pull Request #723 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 149 additions & 19 deletions lightning/src/chain/keysinterface.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,12 +33,13 @@ use util::ser::{Writeable, Writer, Readable};

use chain::transaction::OutPoint;
use ln::chan_utils;
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys};
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys, derive_public_key, derive_public_revocation_key, TxCreationKeys};
use ln::msgs::UnsignedChannelAnnouncement;

use std::sync::atomic::{AtomicUsize, Ordering};
use std::io::Error;
use ln::msgs::DecodeError;
use ln::transaction::{CommitmentInfo, build_commitment_tx, get_commitment_transaction_number_obscure_factor};

/// 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
Expand DownExpand Up@@ -79,7 +80,7 @@ pub enum SpendableOutputDescriptor {
///
/// To derive the revocation_pubkey provided here (which is used in the witness
/// script generation), you must pass the counterparty revocation_basepoint (which appears in the
/// call to ChannelKeys::on_accept) and the provided per_commitment point
/// call to ChannelKeys::ready_channel) and the provided per_commitment point
/// to chan_utils::derive_public_revocation_key.
///
/// The witness script which is hashed and included in the output script_pubkey may be
Expand DownExpand Up@@ -244,6 +245,18 @@ pub trait ChannelKeys : Send+Clone {
// TODO: Add more input vars to enable better checking (preferably removing commitment_tx and
fn sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Create a signature for a holder's commitment transaction and attached HTLC transactions.
fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replaces the previous function.

&self,
commitment_number: u64,
feerate_per_kw: u32,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
pre_keys: &PreCalculatedTxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(Signature, Vec<Signature>), ()>;

/// Same as sign_holder_commitment, but exists only for tests to get access to holder commitment
/// transactions which will be broadcasted later, after the channel has moved on to a newer
/// state. Thus, needs its own method as sign_holder_commitment may enforce that we only ever
Expand DownExpand Up@@ -319,13 +332,15 @@ pub trait ChannelKeys : Send+Clone {
/// protocol.
fn sign_channel_announcement<T: secp256k1::Signing>(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Set the counterparty channel basepoints and counterparty_selected/holder_selected_contest_delay.
/// This is done immediately on incoming channels and as soon as the channel is accepted on outgoing channels.
/// Set the counterparty static channel data, including basepoints,
/// counterparty_selected/holder_selected_contest_delay and funding outpoint.
/// This is done as soon as the funding outpoint is known. Since these are static channel data,
/// they MUST NOT be allowed to change to different values once set.
///
/// We bind holder_selected_contest_delay late here for API convenience.
///
/// Will be called before any signatures are applied.
fn on_accept(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16);
fn ready_channel(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint);
}

/// A trait to describe an object which can get user secrets and key material.
Expand All@@ -349,11 +364,11 @@ pub trait KeysInterface: Send + Sync {
}

#[derive(Clone)]
/// Holds late-bound channel data.
/// This data is available after the channel is known to be accepted, either
/// when receiving an open_channel for an inbound channel or when
/// receiving accept_channel for an outbound channel.
struct AcceptedChannelData {
/// Holds late-bound static channel data.
/// This data is available after the channel is readied, either
/// when receiving an funding_created for an inbound channel or when
/// creating a funding transaction for an outbound channel.
struct StaticChannelData {
/// Counterparty public keys and base points
counterparty_channel_pubkeys: ChannelPublicKeys,
/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
Expand All@@ -365,6 +380,8 @@ struct AcceptedChannelData {
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
holder_selected_contest_delay: u16,
/// Whether the holder is the initiator of this channel
Comment thread
devrandom marked this conversation as resolved.
is_outbound: bool,
}

#[derive(Clone)]
Expand All@@ -385,7 +402,9 @@ pub struct InMemoryChannelKeys {
/// Holder public keys and basepoints
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
/// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance
accepted_channel_data: Option<AcceptedChannelData>,
accepted_channel_data: Option<StaticChannelData>,
/// Funding outpoint, populated on channel funding creation
funding_outpoint: Option<OutPoint>,
/// The total value of this channel
channel_value_satoshis: u64,
/// Key derivation parameters
Expand DownExpand Up@@ -418,6 +437,7 @@ impl InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: None,
funding_outpoint: None,
key_derivation_params,
}
}
Expand All@@ -439,21 +459,83 @@ impl InMemoryChannelKeys {
}

/// Counterparty pubkeys.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.accepted_channel_data.as_ref().unwrap().counterparty_channel_pubkeys }

/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
/// transactions, ie the amount of time that we have to wait to recover our funds if we
/// broadcast a transaction. You'll likely want to pass this to the
/// ln::chan_utils::build*_transaction functions when signing holder's transactions.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().counterparty_selected_contest_delay }

/// The contest_delay value specified by us and applied on transactions broadcastable
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn holder_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().holder_selected_contest_delay }

/// Whether the holder is the initiator
pub fn is_outbound(&self) -> bool { self.accepted_channel_data.as_ref().unwrap().is_outbound }

/// Funding outpoint
/// Will panic if ready_channel wasn't called.
pub fn funding_outpoint(&self) -> &OutPoint { self.funding_outpoint.as_ref().unwrap() }

/// Prepare build information for a commitment tx that the holder can broadcast
pub fn build_holder_commitment_tx<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this need to be in the public interface, or are you imaging this is only a utility function in the InMemory version?

&self,
commitment_number: u64,
per_commitment_point: &PublicKey,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
keys: &TxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(bitcoin::Transaction, Vec<HTLCOutputInCommitment>, Vec<Script>), ()> {
let counterparty_points = self.counterparty_pubkeys();

let to_holder_delayed_pubkey = derive_public_key(
&secp_ctx,
&per_commitment_point,
&self.pubkeys().delayed_payment_basepoint,
).map_err(|_| ())?;

let revocation_pubkey = derive_public_revocation_key(
&secp_ctx,
&per_commitment_point,
&counterparty_points.revocation_basepoint,
).map_err(|_| ())?;

let info = CommitmentInfo {
is_counterparty_broadcaster: false,
to_countersigner_pubkey: counterparty_points.payment_point,
to_countersigner_value_sat: to_counterparty_value_sat,
revocation_pubkey,
to_broadcaster_delayed_pubkey: to_holder_delayed_pubkey,
to_broadcaster_value_sat: to_holder_value_sat,
to_self_delay: self.counterparty_selected_contest_delay(),
htlcs: htlcs.clone(),
};

let obscured_commitment_transaction_number =
self.get_commitment_transaction_number_obscure_factor() ^ commitment_number;
let (tx, htlcs, scripts) = build_commitment_tx(
&keys,
&info,
obscured_commitment_transaction_number,
self.funding_outpoint().into_bitcoin_outpoint(),
);
Ok((tx, htlcs, scripts))
}

fn get_commitment_transaction_number_obscure_factor(&self) -> u64 {
get_commitment_transaction_number_obscure_factor(
&self.pubkeys().payment_point,
&self.counterparty_pubkeys().payment_point,
self.is_outbound(),
)
}
}

impl ChannelKeys for InMemoryChannelKeys {
Expand DownExpand Up@@ -507,6 +589,48 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(holder_commitment_tx.get_holder_sig(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
}

fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_number: u64, feerate_per_kw: u32, to_holder_value_sat: u64, to_counterparty_value_sat: u64, htlcs: &Vec<HTLCOutputInCommitment>, pre_keys: &PreCalculatedTxCreationKeys, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()> {
let per_commitment_point = self.get_per_commitment_point(commitment_number, secp_ctx);
let keys = pre_keys.trust_key_derivation();
let (tx, htlcs, _) = self.build_holder_commitment_tx(
commitment_number,
&per_commitment_point,
to_holder_value_sat,
to_counterparty_value_sat,
htlcs,
keys,
secp_ctx,
)?;

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);
let sighash = hash_to_message!(&bip143::SigHashCache::new(&tx)
.signature_hash(0, &funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
let sig = secp_ctx.sign(&sighash, &self.funding_key);

// We provide a dummy signature for the remote, since we don't require that sig
// to be passed in to this call. It would have been better if HolderCommitmentTransaction
// didn't require the remote sig.
let dummy_sig = Secp256k1::new().sign(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This awkwardness is due to HolderCommitmentTransaction holding counterparty signatures. Not sure what's the best thing here - perhaps separate the signature holding to another struct?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would like to get ride of HolderCommitmentTransaction. It's API can be enforced either by ChannelMonitor or an external signer implementation like InMemoryChannelKeys.

All dynamic state (balances, HTLCs, ...) would be stored in Channel and the rest assumed to be in signer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why? HolderCommitmentTransaction, indeed, holds too much data today (specifically, the transaction itself), but communicating things like the counterparty signatures and the per htlc values has to be done. eg here the function signature has a ton of fields, that may be usefully just shoved into a struct, and HolderCommitmentTransaction is basically that. (I assume we agree here, just noting that we may end up wanting a struct either way).

&secp256k1::Message::from_slice(&[42; 32]).unwrap(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
let htlcs_with_sig = htlcs.iter().map(|h| (h.clone(), Some(dummy_sig.clone()))).collect();
let commitment_tx = HolderCommitmentTransaction::new_missing_holder_sig(
tx,
dummy_sig,
&self.pubkeys().funding_pubkey,
&self.counterparty_pubkeys().funding_pubkey,
keys.clone(),
feerate_per_kw,
htlcs_with_sig,
);
let htlc_sigs_o = self.sign_holder_commitment_htlc_transactions(&commitment_tx, secp_ctx)?;
let htlc_sigs = htlc_sigs_o.iter().map(|o| o.unwrap()).collect();

Ok((sig, htlc_sigs))
}

#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
fn unsafe_sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
Expand DownExpand Up@@ -588,18 +712,21 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(secp_ctx.sign(&msghash, &self.funding_key))
}

fn on_accept(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16) {
assert!(self.accepted_channel_data.is_none(), "Already accepted");
self.accepted_channel_data = Some(AcceptedChannelData {
fn ready_channel(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint) {
assert!(self.accepted_channel_data.is_none(), "Acceptance already noted");
self.accepted_channel_data = Some(StaticChannelData {
counterparty_channel_pubkeys: channel_pubkeys.clone(),
counterparty_selected_contest_delay,
holder_selected_contest_delay,
is_outbound,
});
assert!(self.funding_outpoint.is_none(), "Funding creation already noted");
self.funding_outpoint = Some(funding_outpoint.clone());
}
}

impl_writeable!(AcceptedChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay });
impl_writeable!(StaticChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay, is_outbound });

impl Writeable for InMemoryChannelKeys {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
Expand All@@ -610,6 +737,7 @@ impl Writeable for InMemoryChannelKeys {
self.htlc_base_key.write(writer)?;
self.commitment_seed.write(writer)?;
self.accepted_channel_data.write(writer)?;
self.funding_outpoint.write(writer)?;
self.channel_value_satoshis.write(writer)?;
self.key_derivation_params.0.write(writer)?;
self.key_derivation_params.1.write(writer)?;
Expand All@@ -627,6 +755,7 @@ impl Readable for InMemoryChannelKeys {
let htlc_base_key = Readable::read(reader)?;
let commitment_seed = Readable::read(reader)?;
let counterparty_channel_data = Readable::read(reader)?;
let funding_outpoint = Readable::read(reader)?;
let channel_value_satoshis = Readable::read(reader)?;
let secp_ctx = Secp256k1::signing_only();
let holder_channel_pubkeys =
Expand All@@ -646,6 +775,7 @@ impl Readable for InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: counterparty_channel_data,
funding_outpoint,
key_derivation_params: (params_1, params_2),
})
}
Expand Down
16 changes: 15 additions & 1 deletion lightning/src/ln/chan_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -372,7 +372,8 @@ impl_writeable!(ChannelPublicKeys, 33*5, {


impl TxCreationKeys {
/// Create a new TxCreationKeys from channel base points and the per-commitment point
/// Create per-state keys from channel base points and the per-commitment point
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> Result<TxCreationKeys, SecpError> {
Ok(TxCreationKeys {
per_commitment_point: per_commitment_point.clone(),
Expand All@@ -382,6 +383,19 @@ impl TxCreationKeys {
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base)?,
})
}

/// Generate per-state keys from channel static keys.
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn make_tx_keys<T: secp256k1::Signing + secp256k1::Verification>(per_commitment_point: &PublicKey, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TxCreationKeys, SecpError> {
Comment thread
devrandom marked this conversation as resolved.
TxCreationKeys::derive_new(
&secp_ctx,
&per_commitment_point,
&broadcaster_keys.delayed_payment_basepoint,
&broadcaster_keys.htlc_basepoint,
&countersignatory_keys.revocation_basepoint,
&countersignatory_keys.htlc_basepoint,
)
}
}

/// A script either spendable by the revocation
Expand Down
10 changes: 5 additions & 5 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -582,15 +582,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
where K::Target: KeysInterface<ChanKeySigner = ChanSigner>,
F::Target: FeeEstimator
{
let mut chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: msg.funding_pubkey,
revocation_basepoint: msg.revocation_basepoint,
payment_point: msg.payment_point,
delayed_payment_basepoint: msg.delayed_payment_basepoint,
htlc_basepoint: msg.htlc_basepoint
};
chan_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, config.own_channel_config.our_to_self_delay);
let mut local_config = (*config).channel_options.clone();

if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT {
Expand DownExpand Up@@ -1463,7 +1462,6 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
htlc_basepoint: msg.htlc_basepoint
};

self.holder_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, self.holder_selected_contest_delay);
self.counterparty_pubkeys = Some(counterparty_pubkeys);

self.counterparty_cur_commitment_point = Some(msg.first_per_commitment_point);
Expand DownExpand Up@@ -1518,6 +1516,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
}

let funding_txo = OutPoint{ txid: msg.funding_txid, index: msg.funding_output_index };
self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());

let (counterparty_initial_commitment_tx, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
Expand DownExpand Up@@ -3545,6 +3544,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
}

self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());
let signature = match self.get_outbound_funding_created_signature(logger) {
Ok(res) => res,
Expand DownExpand Up@@ -4647,7 +4647,7 @@ mod tests {
chan.holder_dust_limit_satoshis = 546;

let funding_info = OutPoint{ txid: Txid::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), index: 0 };
chan.funding_txo = Some(funding_info);
chan.funding_txo = Some(funding_info.clone());

let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
Expand All@@ -4656,7 +4656,7 @@ mod tests {
delayed_payment_basepoint: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
htlc_basepoint: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444")
};
chan_keys.on_accept(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay);
chan_keys.ready_channel(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay, false, &funding_info);

assert_eq!(counterparty_pubkeys.payment_point.serialize()[..],
hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]);
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ pub mod peer_handler;
pub mod chan_utils;
pub mod features;
pub(crate) mod onchaintx;
pub mod transaction;

#[cfg(feature = "fuzztarget")]
pub mod peer_channel_encryptor;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Draft: phase-2 signer API by devrandom · Pull Request #723 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 149 additions & 19 deletions lightning/src/chain/keysinterface.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,12 +33,13 @@ use util::ser::{Writeable, Writer, Readable};

use chain::transaction::OutPoint;
use ln::chan_utils;
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys};
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys, derive_public_key, derive_public_revocation_key, TxCreationKeys};
use ln::msgs::UnsignedChannelAnnouncement;

use std::sync::atomic::{AtomicUsize, Ordering};
use std::io::Error;
use ln::msgs::DecodeError;
use ln::transaction::{CommitmentInfo, build_commitment_tx, get_commitment_transaction_number_obscure_factor};

/// 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
Expand DownExpand Up@@ -79,7 +80,7 @@ pub enum SpendableOutputDescriptor {
///
/// To derive the revocation_pubkey provided here (which is used in the witness
/// script generation), you must pass the counterparty revocation_basepoint (which appears in the
/// call to ChannelKeys::on_accept) and the provided per_commitment point
/// call to ChannelKeys::ready_channel) and the provided per_commitment point
/// to chan_utils::derive_public_revocation_key.
///
/// The witness script which is hashed and included in the output script_pubkey may be
Expand DownExpand Up@@ -244,6 +245,18 @@ pub trait ChannelKeys : Send+Clone {
// TODO: Add more input vars to enable better checking (preferably removing commitment_tx and
fn sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Create a signature for a holder's commitment transaction and attached HTLC transactions.
fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replaces the previous function.

&self,
commitment_number: u64,
feerate_per_kw: u32,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
pre_keys: &PreCalculatedTxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(Signature, Vec<Signature>), ()>;

/// Same as sign_holder_commitment, but exists only for tests to get access to holder commitment
/// transactions which will be broadcasted later, after the channel has moved on to a newer
/// state. Thus, needs its own method as sign_holder_commitment may enforce that we only ever
Expand DownExpand Up@@ -319,13 +332,15 @@ pub trait ChannelKeys : Send+Clone {
/// protocol.
fn sign_channel_announcement<T: secp256k1::Signing>(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Set the counterparty channel basepoints and counterparty_selected/holder_selected_contest_delay.
/// This is done immediately on incoming channels and as soon as the channel is accepted on outgoing channels.
/// Set the counterparty static channel data, including basepoints,
/// counterparty_selected/holder_selected_contest_delay and funding outpoint.
/// This is done as soon as the funding outpoint is known. Since these are static channel data,
/// they MUST NOT be allowed to change to different values once set.
///
/// We bind holder_selected_contest_delay late here for API convenience.
///
/// Will be called before any signatures are applied.
fn on_accept(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16);
fn ready_channel(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint);
}

/// A trait to describe an object which can get user secrets and key material.
Expand All@@ -349,11 +364,11 @@ pub trait KeysInterface: Send + Sync {
}

#[derive(Clone)]
/// Holds late-bound channel data.
/// This data is available after the channel is known to be accepted, either
/// when receiving an open_channel for an inbound channel or when
/// receiving accept_channel for an outbound channel.
struct AcceptedChannelData {
/// Holds late-bound static channel data.
/// This data is available after the channel is readied, either
/// when receiving an funding_created for an inbound channel or when
/// creating a funding transaction for an outbound channel.
struct StaticChannelData {
/// Counterparty public keys and base points
counterparty_channel_pubkeys: ChannelPublicKeys,
/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
Expand All@@ -365,6 +380,8 @@ struct AcceptedChannelData {
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
holder_selected_contest_delay: u16,
/// Whether the holder is the initiator of this channel
Comment thread
devrandom marked this conversation as resolved.
is_outbound: bool,
}

#[derive(Clone)]
Expand All@@ -385,7 +402,9 @@ pub struct InMemoryChannelKeys {
/// Holder public keys and basepoints
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
/// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance
accepted_channel_data: Option<AcceptedChannelData>,
accepted_channel_data: Option<StaticChannelData>,
/// Funding outpoint, populated on channel funding creation
funding_outpoint: Option<OutPoint>,
/// The total value of this channel
channel_value_satoshis: u64,
/// Key derivation parameters
Expand DownExpand Up@@ -418,6 +437,7 @@ impl InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: None,
funding_outpoint: None,
key_derivation_params,
}
}
Expand All@@ -439,21 +459,83 @@ impl InMemoryChannelKeys {
}

/// Counterparty pubkeys.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.accepted_channel_data.as_ref().unwrap().counterparty_channel_pubkeys }

/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
/// transactions, ie the amount of time that we have to wait to recover our funds if we
/// broadcast a transaction. You'll likely want to pass this to the
/// ln::chan_utils::build*_transaction functions when signing holder's transactions.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().counterparty_selected_contest_delay }

/// The contest_delay value specified by us and applied on transactions broadcastable
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn holder_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().holder_selected_contest_delay }

/// Whether the holder is the initiator
pub fn is_outbound(&self) -> bool { self.accepted_channel_data.as_ref().unwrap().is_outbound }

/// Funding outpoint
/// Will panic if ready_channel wasn't called.
pub fn funding_outpoint(&self) -> &OutPoint { self.funding_outpoint.as_ref().unwrap() }

/// Prepare build information for a commitment tx that the holder can broadcast
pub fn build_holder_commitment_tx<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this need to be in the public interface, or are you imaging this is only a utility function in the InMemory version?

&self,
commitment_number: u64,
per_commitment_point: &PublicKey,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
keys: &TxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(bitcoin::Transaction, Vec<HTLCOutputInCommitment>, Vec<Script>), ()> {
let counterparty_points = self.counterparty_pubkeys();

let to_holder_delayed_pubkey = derive_public_key(
&secp_ctx,
&per_commitment_point,
&self.pubkeys().delayed_payment_basepoint,
).map_err(|_| ())?;

let revocation_pubkey = derive_public_revocation_key(
&secp_ctx,
&per_commitment_point,
&counterparty_points.revocation_basepoint,
).map_err(|_| ())?;

let info = CommitmentInfo {
is_counterparty_broadcaster: false,
to_countersigner_pubkey: counterparty_points.payment_point,
to_countersigner_value_sat: to_counterparty_value_sat,
revocation_pubkey,
to_broadcaster_delayed_pubkey: to_holder_delayed_pubkey,
to_broadcaster_value_sat: to_holder_value_sat,
to_self_delay: self.counterparty_selected_contest_delay(),
htlcs: htlcs.clone(),
};

let obscured_commitment_transaction_number =
self.get_commitment_transaction_number_obscure_factor() ^ commitment_number;
let (tx, htlcs, scripts) = build_commitment_tx(
&keys,
&info,
obscured_commitment_transaction_number,
self.funding_outpoint().into_bitcoin_outpoint(),
);
Ok((tx, htlcs, scripts))
}

fn get_commitment_transaction_number_obscure_factor(&self) -> u64 {
get_commitment_transaction_number_obscure_factor(
&self.pubkeys().payment_point,
&self.counterparty_pubkeys().payment_point,
self.is_outbound(),
)
}
}

impl ChannelKeys for InMemoryChannelKeys {
Expand DownExpand Up@@ -507,6 +589,48 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(holder_commitment_tx.get_holder_sig(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
}

fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_number: u64, feerate_per_kw: u32, to_holder_value_sat: u64, to_counterparty_value_sat: u64, htlcs: &Vec<HTLCOutputInCommitment>, pre_keys: &PreCalculatedTxCreationKeys, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()> {
let per_commitment_point = self.get_per_commitment_point(commitment_number, secp_ctx);
let keys = pre_keys.trust_key_derivation();
let (tx, htlcs, _) = self.build_holder_commitment_tx(
commitment_number,
&per_commitment_point,
to_holder_value_sat,
to_counterparty_value_sat,
htlcs,
keys,
secp_ctx,
)?;

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);
let sighash = hash_to_message!(&bip143::SigHashCache::new(&tx)
.signature_hash(0, &funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
let sig = secp_ctx.sign(&sighash, &self.funding_key);

// We provide a dummy signature for the remote, since we don't require that sig
// to be passed in to this call. It would have been better if HolderCommitmentTransaction
// didn't require the remote sig.
let dummy_sig = Secp256k1::new().sign(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This awkwardness is due to HolderCommitmentTransaction holding counterparty signatures. Not sure what's the best thing here - perhaps separate the signature holding to another struct?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would like to get ride of HolderCommitmentTransaction. It's API can be enforced either by ChannelMonitor or an external signer implementation like InMemoryChannelKeys.

All dynamic state (balances, HTLCs, ...) would be stored in Channel and the rest assumed to be in signer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why? HolderCommitmentTransaction, indeed, holds too much data today (specifically, the transaction itself), but communicating things like the counterparty signatures and the per htlc values has to be done. eg here the function signature has a ton of fields, that may be usefully just shoved into a struct, and HolderCommitmentTransaction is basically that. (I assume we agree here, just noting that we may end up wanting a struct either way).

&secp256k1::Message::from_slice(&[42; 32]).unwrap(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
let htlcs_with_sig = htlcs.iter().map(|h| (h.clone(), Some(dummy_sig.clone()))).collect();
let commitment_tx = HolderCommitmentTransaction::new_missing_holder_sig(
tx,
dummy_sig,
&self.pubkeys().funding_pubkey,
&self.counterparty_pubkeys().funding_pubkey,
keys.clone(),
feerate_per_kw,
htlcs_with_sig,
);
let htlc_sigs_o = self.sign_holder_commitment_htlc_transactions(&commitment_tx, secp_ctx)?;
let htlc_sigs = htlc_sigs_o.iter().map(|o| o.unwrap()).collect();

Ok((sig, htlc_sigs))
}

#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
fn unsafe_sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
Expand DownExpand Up@@ -588,18 +712,21 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(secp_ctx.sign(&msghash, &self.funding_key))
}

fn on_accept(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16) {
assert!(self.accepted_channel_data.is_none(), "Already accepted");
self.accepted_channel_data = Some(AcceptedChannelData {
fn ready_channel(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint) {
assert!(self.accepted_channel_data.is_none(), "Acceptance already noted");
self.accepted_channel_data = Some(StaticChannelData {
counterparty_channel_pubkeys: channel_pubkeys.clone(),
counterparty_selected_contest_delay,
holder_selected_contest_delay,
is_outbound,
});
assert!(self.funding_outpoint.is_none(), "Funding creation already noted");
self.funding_outpoint = Some(funding_outpoint.clone());
}
}

impl_writeable!(AcceptedChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay });
impl_writeable!(StaticChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay, is_outbound });

impl Writeable for InMemoryChannelKeys {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
Expand All@@ -610,6 +737,7 @@ impl Writeable for InMemoryChannelKeys {
self.htlc_base_key.write(writer)?;
self.commitment_seed.write(writer)?;
self.accepted_channel_data.write(writer)?;
self.funding_outpoint.write(writer)?;
self.channel_value_satoshis.write(writer)?;
self.key_derivation_params.0.write(writer)?;
self.key_derivation_params.1.write(writer)?;
Expand All@@ -627,6 +755,7 @@ impl Readable for InMemoryChannelKeys {
let htlc_base_key = Readable::read(reader)?;
let commitment_seed = Readable::read(reader)?;
let counterparty_channel_data = Readable::read(reader)?;
let funding_outpoint = Readable::read(reader)?;
let channel_value_satoshis = Readable::read(reader)?;
let secp_ctx = Secp256k1::signing_only();
let holder_channel_pubkeys =
Expand All@@ -646,6 +775,7 @@ impl Readable for InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: counterparty_channel_data,
funding_outpoint,
key_derivation_params: (params_1, params_2),
})
}
Expand Down
16 changes: 15 additions & 1 deletion lightning/src/ln/chan_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -372,7 +372,8 @@ impl_writeable!(ChannelPublicKeys, 33*5, {


impl TxCreationKeys {
/// Create a new TxCreationKeys from channel base points and the per-commitment point
/// Create per-state keys from channel base points and the per-commitment point
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> Result<TxCreationKeys, SecpError> {
Ok(TxCreationKeys {
per_commitment_point: per_commitment_point.clone(),
Expand All@@ -382,6 +383,19 @@ impl TxCreationKeys {
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base)?,
})
}

/// Generate per-state keys from channel static keys.
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn make_tx_keys<T: secp256k1::Signing + secp256k1::Verification>(per_commitment_point: &PublicKey, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TxCreationKeys, SecpError> {
Comment thread
devrandom marked this conversation as resolved.
TxCreationKeys::derive_new(
&secp_ctx,
&per_commitment_point,
&broadcaster_keys.delayed_payment_basepoint,
&broadcaster_keys.htlc_basepoint,
&countersignatory_keys.revocation_basepoint,
&countersignatory_keys.htlc_basepoint,
)
}
}

/// A script either spendable by the revocation
Expand Down
10 changes: 5 additions & 5 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -582,15 +582,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
where K::Target: KeysInterface<ChanKeySigner = ChanSigner>,
F::Target: FeeEstimator
{
let mut chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: msg.funding_pubkey,
revocation_basepoint: msg.revocation_basepoint,
payment_point: msg.payment_point,
delayed_payment_basepoint: msg.delayed_payment_basepoint,
htlc_basepoint: msg.htlc_basepoint
};
chan_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, config.own_channel_config.our_to_self_delay);
let mut local_config = (*config).channel_options.clone();

if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT {
Expand DownExpand Up@@ -1463,7 +1462,6 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
htlc_basepoint: msg.htlc_basepoint
};

self.holder_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, self.holder_selected_contest_delay);
self.counterparty_pubkeys = Some(counterparty_pubkeys);

self.counterparty_cur_commitment_point = Some(msg.first_per_commitment_point);
Expand DownExpand Up@@ -1518,6 +1516,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
}

let funding_txo = OutPoint{ txid: msg.funding_txid, index: msg.funding_output_index };
self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());

let (counterparty_initial_commitment_tx, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
Expand DownExpand Up@@ -3545,6 +3544,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
}

self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());
let signature = match self.get_outbound_funding_created_signature(logger) {
Ok(res) => res,
Expand DownExpand Up@@ -4647,7 +4647,7 @@ mod tests {
chan.holder_dust_limit_satoshis = 546;

let funding_info = OutPoint{ txid: Txid::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), index: 0 };
chan.funding_txo = Some(funding_info);
chan.funding_txo = Some(funding_info.clone());

let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
Expand All@@ -4656,7 +4656,7 @@ mod tests {
delayed_payment_basepoint: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
htlc_basepoint: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444")
};
chan_keys.on_accept(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay);
chan_keys.ready_channel(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay, false, &funding_info);

assert_eq!(counterparty_pubkeys.payment_point.serialize()[..],
hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]);
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ pub mod peer_handler;
pub mod chan_utils;
pub mod features;
pub(crate) mod onchaintx;
pub mod transaction;

#[cfg(feature = "fuzztarget")]
pub mod peer_channel_encryptor;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Draft: phase-2 signer API by devrandom · Pull Request #723 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 149 additions & 19 deletions lightning/src/chain/keysinterface.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,12 +33,13 @@ use util::ser::{Writeable, Writer, Readable};

use chain::transaction::OutPoint;
use ln::chan_utils;
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys};
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys, derive_public_key, derive_public_revocation_key, TxCreationKeys};
use ln::msgs::UnsignedChannelAnnouncement;

use std::sync::atomic::{AtomicUsize, Ordering};
use std::io::Error;
use ln::msgs::DecodeError;
use ln::transaction::{CommitmentInfo, build_commitment_tx, get_commitment_transaction_number_obscure_factor};

/// 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
Expand DownExpand Up@@ -79,7 +80,7 @@ pub enum SpendableOutputDescriptor {
///
/// To derive the revocation_pubkey provided here (which is used in the witness
/// script generation), you must pass the counterparty revocation_basepoint (which appears in the
/// call to ChannelKeys::on_accept) and the provided per_commitment point
/// call to ChannelKeys::ready_channel) and the provided per_commitment point
/// to chan_utils::derive_public_revocation_key.
///
/// The witness script which is hashed and included in the output script_pubkey may be
Expand DownExpand Up@@ -244,6 +245,18 @@ pub trait ChannelKeys : Send+Clone {
// TODO: Add more input vars to enable better checking (preferably removing commitment_tx and
fn sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Create a signature for a holder's commitment transaction and attached HTLC transactions.
fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replaces the previous function.

&self,
commitment_number: u64,
feerate_per_kw: u32,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
pre_keys: &PreCalculatedTxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(Signature, Vec<Signature>), ()>;

/// Same as sign_holder_commitment, but exists only for tests to get access to holder commitment
/// transactions which will be broadcasted later, after the channel has moved on to a newer
/// state. Thus, needs its own method as sign_holder_commitment may enforce that we only ever
Expand DownExpand Up@@ -319,13 +332,15 @@ pub trait ChannelKeys : Send+Clone {
/// protocol.
fn sign_channel_announcement<T: secp256k1::Signing>(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Set the counterparty channel basepoints and counterparty_selected/holder_selected_contest_delay.
/// This is done immediately on incoming channels and as soon as the channel is accepted on outgoing channels.
/// Set the counterparty static channel data, including basepoints,
/// counterparty_selected/holder_selected_contest_delay and funding outpoint.
/// This is done as soon as the funding outpoint is known. Since these are static channel data,
/// they MUST NOT be allowed to change to different values once set.
///
/// We bind holder_selected_contest_delay late here for API convenience.
///
/// Will be called before any signatures are applied.
fn on_accept(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16);
fn ready_channel(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint);
}

/// A trait to describe an object which can get user secrets and key material.
Expand All@@ -349,11 +364,11 @@ pub trait KeysInterface: Send + Sync {
}

#[derive(Clone)]
/// Holds late-bound channel data.
/// This data is available after the channel is known to be accepted, either
/// when receiving an open_channel for an inbound channel or when
/// receiving accept_channel for an outbound channel.
struct AcceptedChannelData {
/// Holds late-bound static channel data.
/// This data is available after the channel is readied, either
/// when receiving an funding_created for an inbound channel or when
/// creating a funding transaction for an outbound channel.
struct StaticChannelData {
/// Counterparty public keys and base points
counterparty_channel_pubkeys: ChannelPublicKeys,
/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
Expand All@@ -365,6 +380,8 @@ struct AcceptedChannelData {
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
holder_selected_contest_delay: u16,
/// Whether the holder is the initiator of this channel
Comment thread
devrandom marked this conversation as resolved.
is_outbound: bool,
}

#[derive(Clone)]
Expand All@@ -385,7 +402,9 @@ pub struct InMemoryChannelKeys {
/// Holder public keys and basepoints
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
/// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance
accepted_channel_data: Option<AcceptedChannelData>,
accepted_channel_data: Option<StaticChannelData>,
/// Funding outpoint, populated on channel funding creation
funding_outpoint: Option<OutPoint>,
/// The total value of this channel
channel_value_satoshis: u64,
/// Key derivation parameters
Expand DownExpand Up@@ -418,6 +437,7 @@ impl InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: None,
funding_outpoint: None,
key_derivation_params,
}
}
Expand All@@ -439,21 +459,83 @@ impl InMemoryChannelKeys {
}

/// Counterparty pubkeys.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.accepted_channel_data.as_ref().unwrap().counterparty_channel_pubkeys }

/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
/// transactions, ie the amount of time that we have to wait to recover our funds if we
/// broadcast a transaction. You'll likely want to pass this to the
/// ln::chan_utils::build*_transaction functions when signing holder's transactions.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().counterparty_selected_contest_delay }

/// The contest_delay value specified by us and applied on transactions broadcastable
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn holder_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().holder_selected_contest_delay }

/// Whether the holder is the initiator
pub fn is_outbound(&self) -> bool { self.accepted_channel_data.as_ref().unwrap().is_outbound }

/// Funding outpoint
/// Will panic if ready_channel wasn't called.
pub fn funding_outpoint(&self) -> &OutPoint { self.funding_outpoint.as_ref().unwrap() }

/// Prepare build information for a commitment tx that the holder can broadcast
pub fn build_holder_commitment_tx<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this need to be in the public interface, or are you imaging this is only a utility function in the InMemory version?

&self,
commitment_number: u64,
per_commitment_point: &PublicKey,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
keys: &TxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(bitcoin::Transaction, Vec<HTLCOutputInCommitment>, Vec<Script>), ()> {
let counterparty_points = self.counterparty_pubkeys();

let to_holder_delayed_pubkey = derive_public_key(
&secp_ctx,
&per_commitment_point,
&self.pubkeys().delayed_payment_basepoint,
).map_err(|_| ())?;

let revocation_pubkey = derive_public_revocation_key(
&secp_ctx,
&per_commitment_point,
&counterparty_points.revocation_basepoint,
).map_err(|_| ())?;

let info = CommitmentInfo {
is_counterparty_broadcaster: false,
to_countersigner_pubkey: counterparty_points.payment_point,
to_countersigner_value_sat: to_counterparty_value_sat,
revocation_pubkey,
to_broadcaster_delayed_pubkey: to_holder_delayed_pubkey,
to_broadcaster_value_sat: to_holder_value_sat,
to_self_delay: self.counterparty_selected_contest_delay(),
htlcs: htlcs.clone(),
};

let obscured_commitment_transaction_number =
self.get_commitment_transaction_number_obscure_factor() ^ commitment_number;
let (tx, htlcs, scripts) = build_commitment_tx(
&keys,
&info,
obscured_commitment_transaction_number,
self.funding_outpoint().into_bitcoin_outpoint(),
);
Ok((tx, htlcs, scripts))
}

fn get_commitment_transaction_number_obscure_factor(&self) -> u64 {
get_commitment_transaction_number_obscure_factor(
&self.pubkeys().payment_point,
&self.counterparty_pubkeys().payment_point,
self.is_outbound(),
)
}
}

impl ChannelKeys for InMemoryChannelKeys {
Expand DownExpand Up@@ -507,6 +589,48 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(holder_commitment_tx.get_holder_sig(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
}

fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_number: u64, feerate_per_kw: u32, to_holder_value_sat: u64, to_counterparty_value_sat: u64, htlcs: &Vec<HTLCOutputInCommitment>, pre_keys: &PreCalculatedTxCreationKeys, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()> {
let per_commitment_point = self.get_per_commitment_point(commitment_number, secp_ctx);
let keys = pre_keys.trust_key_derivation();
let (tx, htlcs, _) = self.build_holder_commitment_tx(
commitment_number,
&per_commitment_point,
to_holder_value_sat,
to_counterparty_value_sat,
htlcs,
keys,
secp_ctx,
)?;

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);
let sighash = hash_to_message!(&bip143::SigHashCache::new(&tx)
.signature_hash(0, &funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
let sig = secp_ctx.sign(&sighash, &self.funding_key);

// We provide a dummy signature for the remote, since we don't require that sig
// to be passed in to this call. It would have been better if HolderCommitmentTransaction
// didn't require the remote sig.
let dummy_sig = Secp256k1::new().sign(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This awkwardness is due to HolderCommitmentTransaction holding counterparty signatures. Not sure what's the best thing here - perhaps separate the signature holding to another struct?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would like to get ride of HolderCommitmentTransaction. It's API can be enforced either by ChannelMonitor or an external signer implementation like InMemoryChannelKeys.

All dynamic state (balances, HTLCs, ...) would be stored in Channel and the rest assumed to be in signer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why? HolderCommitmentTransaction, indeed, holds too much data today (specifically, the transaction itself), but communicating things like the counterparty signatures and the per htlc values has to be done. eg here the function signature has a ton of fields, that may be usefully just shoved into a struct, and HolderCommitmentTransaction is basically that. (I assume we agree here, just noting that we may end up wanting a struct either way).

&secp256k1::Message::from_slice(&[42; 32]).unwrap(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
let htlcs_with_sig = htlcs.iter().map(|h| (h.clone(), Some(dummy_sig.clone()))).collect();
let commitment_tx = HolderCommitmentTransaction::new_missing_holder_sig(
tx,
dummy_sig,
&self.pubkeys().funding_pubkey,
&self.counterparty_pubkeys().funding_pubkey,
keys.clone(),
feerate_per_kw,
htlcs_with_sig,
);
let htlc_sigs_o = self.sign_holder_commitment_htlc_transactions(&commitment_tx, secp_ctx)?;
let htlc_sigs = htlc_sigs_o.iter().map(|o| o.unwrap()).collect();

Ok((sig, htlc_sigs))
}

#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
fn unsafe_sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
Expand DownExpand Up@@ -588,18 +712,21 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(secp_ctx.sign(&msghash, &self.funding_key))
}

fn on_accept(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16) {
assert!(self.accepted_channel_data.is_none(), "Already accepted");
self.accepted_channel_data = Some(AcceptedChannelData {
fn ready_channel(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint) {
assert!(self.accepted_channel_data.is_none(), "Acceptance already noted");
self.accepted_channel_data = Some(StaticChannelData {
counterparty_channel_pubkeys: channel_pubkeys.clone(),
counterparty_selected_contest_delay,
holder_selected_contest_delay,
is_outbound,
});
assert!(self.funding_outpoint.is_none(), "Funding creation already noted");
self.funding_outpoint = Some(funding_outpoint.clone());
}
}

impl_writeable!(AcceptedChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay });
impl_writeable!(StaticChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay, is_outbound });

impl Writeable for InMemoryChannelKeys {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
Expand All@@ -610,6 +737,7 @@ impl Writeable for InMemoryChannelKeys {
self.htlc_base_key.write(writer)?;
self.commitment_seed.write(writer)?;
self.accepted_channel_data.write(writer)?;
self.funding_outpoint.write(writer)?;
self.channel_value_satoshis.write(writer)?;
self.key_derivation_params.0.write(writer)?;
self.key_derivation_params.1.write(writer)?;
Expand All@@ -627,6 +755,7 @@ impl Readable for InMemoryChannelKeys {
let htlc_base_key = Readable::read(reader)?;
let commitment_seed = Readable::read(reader)?;
let counterparty_channel_data = Readable::read(reader)?;
let funding_outpoint = Readable::read(reader)?;
let channel_value_satoshis = Readable::read(reader)?;
let secp_ctx = Secp256k1::signing_only();
let holder_channel_pubkeys =
Expand All@@ -646,6 +775,7 @@ impl Readable for InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: counterparty_channel_data,
funding_outpoint,
key_derivation_params: (params_1, params_2),
})
}
Expand Down
16 changes: 15 additions & 1 deletion lightning/src/ln/chan_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -372,7 +372,8 @@ impl_writeable!(ChannelPublicKeys, 33*5, {


impl TxCreationKeys {
/// Create a new TxCreationKeys from channel base points and the per-commitment point
/// Create per-state keys from channel base points and the per-commitment point
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> Result<TxCreationKeys, SecpError> {
Ok(TxCreationKeys {
per_commitment_point: per_commitment_point.clone(),
Expand All@@ -382,6 +383,19 @@ impl TxCreationKeys {
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base)?,
})
}

/// Generate per-state keys from channel static keys.
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn make_tx_keys<T: secp256k1::Signing + secp256k1::Verification>(per_commitment_point: &PublicKey, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TxCreationKeys, SecpError> {
Comment thread
devrandom marked this conversation as resolved.
TxCreationKeys::derive_new(
&secp_ctx,
&per_commitment_point,
&broadcaster_keys.delayed_payment_basepoint,
&broadcaster_keys.htlc_basepoint,
&countersignatory_keys.revocation_basepoint,
&countersignatory_keys.htlc_basepoint,
)
}
}

/// A script either spendable by the revocation
Expand Down
10 changes: 5 additions & 5 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -582,15 +582,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
where K::Target: KeysInterface<ChanKeySigner = ChanSigner>,
F::Target: FeeEstimator
{
let mut chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: msg.funding_pubkey,
revocation_basepoint: msg.revocation_basepoint,
payment_point: msg.payment_point,
delayed_payment_basepoint: msg.delayed_payment_basepoint,
htlc_basepoint: msg.htlc_basepoint
};
chan_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, config.own_channel_config.our_to_self_delay);
let mut local_config = (*config).channel_options.clone();

if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT {
Expand DownExpand Up@@ -1463,7 +1462,6 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
htlc_basepoint: msg.htlc_basepoint
};

self.holder_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, self.holder_selected_contest_delay);
self.counterparty_pubkeys = Some(counterparty_pubkeys);

self.counterparty_cur_commitment_point = Some(msg.first_per_commitment_point);
Expand DownExpand Up@@ -1518,6 +1516,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
}

let funding_txo = OutPoint{ txid: msg.funding_txid, index: msg.funding_output_index };
self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());

let (counterparty_initial_commitment_tx, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
Expand DownExpand Up@@ -3545,6 +3544,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
}

self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());
let signature = match self.get_outbound_funding_created_signature(logger) {
Ok(res) => res,
Expand DownExpand Up@@ -4647,7 +4647,7 @@ mod tests {
chan.holder_dust_limit_satoshis = 546;

let funding_info = OutPoint{ txid: Txid::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), index: 0 };
chan.funding_txo = Some(funding_info);
chan.funding_txo = Some(funding_info.clone());

let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
Expand All@@ -4656,7 +4656,7 @@ mod tests {
delayed_payment_basepoint: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
htlc_basepoint: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444")
};
chan_keys.on_accept(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay);
chan_keys.ready_channel(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay, false, &funding_info);

assert_eq!(counterparty_pubkeys.payment_point.serialize()[..],
hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]);
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ pub mod peer_handler;
pub mod chan_utils;
pub mod features;
pub(crate) mod onchaintx;
pub mod transaction;

#[cfg(feature = "fuzztarget")]
pub mod peer_channel_encryptor;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Draft: phase-2 signer API by devrandom · Pull Request #723 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 149 additions & 19 deletions lightning/src/chain/keysinterface.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,12 +33,13 @@ use util::ser::{Writeable, Writer, Readable};

use chain::transaction::OutPoint;
use ln::chan_utils;
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys};
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys, derive_public_key, derive_public_revocation_key, TxCreationKeys};
use ln::msgs::UnsignedChannelAnnouncement;

use std::sync::atomic::{AtomicUsize, Ordering};
use std::io::Error;
use ln::msgs::DecodeError;
use ln::transaction::{CommitmentInfo, build_commitment_tx, get_commitment_transaction_number_obscure_factor};

/// 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
Expand DownExpand Up@@ -79,7 +80,7 @@ pub enum SpendableOutputDescriptor {
///
/// To derive the revocation_pubkey provided here (which is used in the witness
/// script generation), you must pass the counterparty revocation_basepoint (which appears in the
/// call to ChannelKeys::on_accept) and the provided per_commitment point
/// call to ChannelKeys::ready_channel) and the provided per_commitment point
/// to chan_utils::derive_public_revocation_key.
///
/// The witness script which is hashed and included in the output script_pubkey may be
Expand DownExpand Up@@ -244,6 +245,18 @@ pub trait ChannelKeys : Send+Clone {
// TODO: Add more input vars to enable better checking (preferably removing commitment_tx and
fn sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Create a signature for a holder's commitment transaction and attached HTLC transactions.
fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replaces the previous function.

&self,
commitment_number: u64,
feerate_per_kw: u32,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
pre_keys: &PreCalculatedTxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(Signature, Vec<Signature>), ()>;

/// Same as sign_holder_commitment, but exists only for tests to get access to holder commitment
/// transactions which will be broadcasted later, after the channel has moved on to a newer
/// state. Thus, needs its own method as sign_holder_commitment may enforce that we only ever
Expand DownExpand Up@@ -319,13 +332,15 @@ pub trait ChannelKeys : Send+Clone {
/// protocol.
fn sign_channel_announcement<T: secp256k1::Signing>(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Set the counterparty channel basepoints and counterparty_selected/holder_selected_contest_delay.
/// This is done immediately on incoming channels and as soon as the channel is accepted on outgoing channels.
/// Set the counterparty static channel data, including basepoints,
/// counterparty_selected/holder_selected_contest_delay and funding outpoint.
/// This is done as soon as the funding outpoint is known. Since these are static channel data,
/// they MUST NOT be allowed to change to different values once set.
///
/// We bind holder_selected_contest_delay late here for API convenience.
///
/// Will be called before any signatures are applied.
fn on_accept(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16);
fn ready_channel(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint);
}

/// A trait to describe an object which can get user secrets and key material.
Expand All@@ -349,11 +364,11 @@ pub trait KeysInterface: Send + Sync {
}

#[derive(Clone)]
/// Holds late-bound channel data.
/// This data is available after the channel is known to be accepted, either
/// when receiving an open_channel for an inbound channel or when
/// receiving accept_channel for an outbound channel.
struct AcceptedChannelData {
/// Holds late-bound static channel data.
/// This data is available after the channel is readied, either
/// when receiving an funding_created for an inbound channel or when
/// creating a funding transaction for an outbound channel.
struct StaticChannelData {
/// Counterparty public keys and base points
counterparty_channel_pubkeys: ChannelPublicKeys,
/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
Expand All@@ -365,6 +380,8 @@ struct AcceptedChannelData {
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
holder_selected_contest_delay: u16,
/// Whether the holder is the initiator of this channel
Comment thread
devrandom marked this conversation as resolved.
is_outbound: bool,
}

#[derive(Clone)]
Expand All@@ -385,7 +402,9 @@ pub struct InMemoryChannelKeys {
/// Holder public keys and basepoints
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
/// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance
accepted_channel_data: Option<AcceptedChannelData>,
accepted_channel_data: Option<StaticChannelData>,
/// Funding outpoint, populated on channel funding creation
funding_outpoint: Option<OutPoint>,
/// The total value of this channel
channel_value_satoshis: u64,
/// Key derivation parameters
Expand DownExpand Up@@ -418,6 +437,7 @@ impl InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: None,
funding_outpoint: None,
key_derivation_params,
}
}
Expand All@@ -439,21 +459,83 @@ impl InMemoryChannelKeys {
}

/// Counterparty pubkeys.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.accepted_channel_data.as_ref().unwrap().counterparty_channel_pubkeys }

/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
/// transactions, ie the amount of time that we have to wait to recover our funds if we
/// broadcast a transaction. You'll likely want to pass this to the
/// ln::chan_utils::build*_transaction functions when signing holder's transactions.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().counterparty_selected_contest_delay }

/// The contest_delay value specified by us and applied on transactions broadcastable
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn holder_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().holder_selected_contest_delay }

/// Whether the holder is the initiator
pub fn is_outbound(&self) -> bool { self.accepted_channel_data.as_ref().unwrap().is_outbound }

/// Funding outpoint
/// Will panic if ready_channel wasn't called.
pub fn funding_outpoint(&self) -> &OutPoint { self.funding_outpoint.as_ref().unwrap() }

/// Prepare build information for a commitment tx that the holder can broadcast
pub fn build_holder_commitment_tx<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this need to be in the public interface, or are you imaging this is only a utility function in the InMemory version?

&self,
commitment_number: u64,
per_commitment_point: &PublicKey,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
keys: &TxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(bitcoin::Transaction, Vec<HTLCOutputInCommitment>, Vec<Script>), ()> {
let counterparty_points = self.counterparty_pubkeys();

let to_holder_delayed_pubkey = derive_public_key(
&secp_ctx,
&per_commitment_point,
&self.pubkeys().delayed_payment_basepoint,
).map_err(|_| ())?;

let revocation_pubkey = derive_public_revocation_key(
&secp_ctx,
&per_commitment_point,
&counterparty_points.revocation_basepoint,
).map_err(|_| ())?;

let info = CommitmentInfo {
is_counterparty_broadcaster: false,
to_countersigner_pubkey: counterparty_points.payment_point,
to_countersigner_value_sat: to_counterparty_value_sat,
revocation_pubkey,
to_broadcaster_delayed_pubkey: to_holder_delayed_pubkey,
to_broadcaster_value_sat: to_holder_value_sat,
to_self_delay: self.counterparty_selected_contest_delay(),
htlcs: htlcs.clone(),
};

let obscured_commitment_transaction_number =
self.get_commitment_transaction_number_obscure_factor() ^ commitment_number;
let (tx, htlcs, scripts) = build_commitment_tx(
&keys,
&info,
obscured_commitment_transaction_number,
self.funding_outpoint().into_bitcoin_outpoint(),
);
Ok((tx, htlcs, scripts))
}

fn get_commitment_transaction_number_obscure_factor(&self) -> u64 {
get_commitment_transaction_number_obscure_factor(
&self.pubkeys().payment_point,
&self.counterparty_pubkeys().payment_point,
self.is_outbound(),
)
}
}

impl ChannelKeys for InMemoryChannelKeys {
Expand DownExpand Up@@ -507,6 +589,48 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(holder_commitment_tx.get_holder_sig(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
}

fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_number: u64, feerate_per_kw: u32, to_holder_value_sat: u64, to_counterparty_value_sat: u64, htlcs: &Vec<HTLCOutputInCommitment>, pre_keys: &PreCalculatedTxCreationKeys, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()> {
let per_commitment_point = self.get_per_commitment_point(commitment_number, secp_ctx);
let keys = pre_keys.trust_key_derivation();
let (tx, htlcs, _) = self.build_holder_commitment_tx(
commitment_number,
&per_commitment_point,
to_holder_value_sat,
to_counterparty_value_sat,
htlcs,
keys,
secp_ctx,
)?;

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);
let sighash = hash_to_message!(&bip143::SigHashCache::new(&tx)
.signature_hash(0, &funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
let sig = secp_ctx.sign(&sighash, &self.funding_key);

// We provide a dummy signature for the remote, since we don't require that sig
// to be passed in to this call. It would have been better if HolderCommitmentTransaction
// didn't require the remote sig.
let dummy_sig = Secp256k1::new().sign(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This awkwardness is due to HolderCommitmentTransaction holding counterparty signatures. Not sure what's the best thing here - perhaps separate the signature holding to another struct?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would like to get ride of HolderCommitmentTransaction. It's API can be enforced either by ChannelMonitor or an external signer implementation like InMemoryChannelKeys.

All dynamic state (balances, HTLCs, ...) would be stored in Channel and the rest assumed to be in signer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why? HolderCommitmentTransaction, indeed, holds too much data today (specifically, the transaction itself), but communicating things like the counterparty signatures and the per htlc values has to be done. eg here the function signature has a ton of fields, that may be usefully just shoved into a struct, and HolderCommitmentTransaction is basically that. (I assume we agree here, just noting that we may end up wanting a struct either way).

&secp256k1::Message::from_slice(&[42; 32]).unwrap(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
let htlcs_with_sig = htlcs.iter().map(|h| (h.clone(), Some(dummy_sig.clone()))).collect();
let commitment_tx = HolderCommitmentTransaction::new_missing_holder_sig(
tx,
dummy_sig,
&self.pubkeys().funding_pubkey,
&self.counterparty_pubkeys().funding_pubkey,
keys.clone(),
feerate_per_kw,
htlcs_with_sig,
);
let htlc_sigs_o = self.sign_holder_commitment_htlc_transactions(&commitment_tx, secp_ctx)?;
let htlc_sigs = htlc_sigs_o.iter().map(|o| o.unwrap()).collect();

Ok((sig, htlc_sigs))
}

#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
fn unsafe_sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
Expand DownExpand Up@@ -588,18 +712,21 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(secp_ctx.sign(&msghash, &self.funding_key))
}

fn on_accept(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16) {
assert!(self.accepted_channel_data.is_none(), "Already accepted");
self.accepted_channel_data = Some(AcceptedChannelData {
fn ready_channel(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint) {
assert!(self.accepted_channel_data.is_none(), "Acceptance already noted");
self.accepted_channel_data = Some(StaticChannelData {
counterparty_channel_pubkeys: channel_pubkeys.clone(),
counterparty_selected_contest_delay,
holder_selected_contest_delay,
is_outbound,
});
assert!(self.funding_outpoint.is_none(), "Funding creation already noted");
self.funding_outpoint = Some(funding_outpoint.clone());
}
}

impl_writeable!(AcceptedChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay });
impl_writeable!(StaticChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay, is_outbound });

impl Writeable for InMemoryChannelKeys {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
Expand All@@ -610,6 +737,7 @@ impl Writeable for InMemoryChannelKeys {
self.htlc_base_key.write(writer)?;
self.commitment_seed.write(writer)?;
self.accepted_channel_data.write(writer)?;
self.funding_outpoint.write(writer)?;
self.channel_value_satoshis.write(writer)?;
self.key_derivation_params.0.write(writer)?;
self.key_derivation_params.1.write(writer)?;
Expand All@@ -627,6 +755,7 @@ impl Readable for InMemoryChannelKeys {
let htlc_base_key = Readable::read(reader)?;
let commitment_seed = Readable::read(reader)?;
let counterparty_channel_data = Readable::read(reader)?;
let funding_outpoint = Readable::read(reader)?;
let channel_value_satoshis = Readable::read(reader)?;
let secp_ctx = Secp256k1::signing_only();
let holder_channel_pubkeys =
Expand All@@ -646,6 +775,7 @@ impl Readable for InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: counterparty_channel_data,
funding_outpoint,
key_derivation_params: (params_1, params_2),
})
}
Expand Down
16 changes: 15 additions & 1 deletion lightning/src/ln/chan_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -372,7 +372,8 @@ impl_writeable!(ChannelPublicKeys, 33*5, {


impl TxCreationKeys {
/// Create a new TxCreationKeys from channel base points and the per-commitment point
/// Create per-state keys from channel base points and the per-commitment point
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> Result<TxCreationKeys, SecpError> {
Ok(TxCreationKeys {
per_commitment_point: per_commitment_point.clone(),
Expand All@@ -382,6 +383,19 @@ impl TxCreationKeys {
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base)?,
})
}

/// Generate per-state keys from channel static keys.
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn make_tx_keys<T: secp256k1::Signing + secp256k1::Verification>(per_commitment_point: &PublicKey, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TxCreationKeys, SecpError> {
Comment thread
devrandom marked this conversation as resolved.
TxCreationKeys::derive_new(
&secp_ctx,
&per_commitment_point,
&broadcaster_keys.delayed_payment_basepoint,
&broadcaster_keys.htlc_basepoint,
&countersignatory_keys.revocation_basepoint,
&countersignatory_keys.htlc_basepoint,
)
}
}

/// A script either spendable by the revocation
Expand Down
10 changes: 5 additions & 5 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -582,15 +582,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
where K::Target: KeysInterface<ChanKeySigner = ChanSigner>,
F::Target: FeeEstimator
{
let mut chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: msg.funding_pubkey,
revocation_basepoint: msg.revocation_basepoint,
payment_point: msg.payment_point,
delayed_payment_basepoint: msg.delayed_payment_basepoint,
htlc_basepoint: msg.htlc_basepoint
};
chan_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, config.own_channel_config.our_to_self_delay);
let mut local_config = (*config).channel_options.clone();

if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT {
Expand DownExpand Up@@ -1463,7 +1462,6 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
htlc_basepoint: msg.htlc_basepoint
};

self.holder_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, self.holder_selected_contest_delay);
self.counterparty_pubkeys = Some(counterparty_pubkeys);

self.counterparty_cur_commitment_point = Some(msg.first_per_commitment_point);
Expand DownExpand Up@@ -1518,6 +1516,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
}

let funding_txo = OutPoint{ txid: msg.funding_txid, index: msg.funding_output_index };
self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());

let (counterparty_initial_commitment_tx, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
Expand DownExpand Up@@ -3545,6 +3544,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
}

self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());
let signature = match self.get_outbound_funding_created_signature(logger) {
Ok(res) => res,
Expand DownExpand Up@@ -4647,7 +4647,7 @@ mod tests {
chan.holder_dust_limit_satoshis = 546;

let funding_info = OutPoint{ txid: Txid::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), index: 0 };
chan.funding_txo = Some(funding_info);
chan.funding_txo = Some(funding_info.clone());

let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
Expand All@@ -4656,7 +4656,7 @@ mod tests {
delayed_payment_basepoint: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
htlc_basepoint: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444")
};
chan_keys.on_accept(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay);
chan_keys.ready_channel(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay, false, &funding_info);

assert_eq!(counterparty_pubkeys.payment_point.serialize()[..],
hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]);
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ pub mod peer_handler;
pub mod chan_utils;
pub mod features;
pub(crate) mod onchaintx;
pub mod transaction;

#[cfg(feature = "fuzztarget")]
pub mod peer_channel_encryptor;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Draft: phase-2 signer API by devrandom · Pull Request #723 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 149 additions & 19 deletions lightning/src/chain/keysinterface.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,12 +33,13 @@ use util::ser::{Writeable, Writer, Readable};

use chain::transaction::OutPoint;
use ln::chan_utils;
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys};
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys, derive_public_key, derive_public_revocation_key, TxCreationKeys};
use ln::msgs::UnsignedChannelAnnouncement;

use std::sync::atomic::{AtomicUsize, Ordering};
use std::io::Error;
use ln::msgs::DecodeError;
use ln::transaction::{CommitmentInfo, build_commitment_tx, get_commitment_transaction_number_obscure_factor};

/// 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
Expand DownExpand Up@@ -79,7 +80,7 @@ pub enum SpendableOutputDescriptor {
///
/// To derive the revocation_pubkey provided here (which is used in the witness
/// script generation), you must pass the counterparty revocation_basepoint (which appears in the
/// call to ChannelKeys::on_accept) and the provided per_commitment point
/// call to ChannelKeys::ready_channel) and the provided per_commitment point
/// to chan_utils::derive_public_revocation_key.
///
/// The witness script which is hashed and included in the output script_pubkey may be
Expand DownExpand Up@@ -244,6 +245,18 @@ pub trait ChannelKeys : Send+Clone {
// TODO: Add more input vars to enable better checking (preferably removing commitment_tx and
fn sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Create a signature for a holder's commitment transaction and attached HTLC transactions.
fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replaces the previous function.

&self,
commitment_number: u64,
feerate_per_kw: u32,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
pre_keys: &PreCalculatedTxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(Signature, Vec<Signature>), ()>;

/// Same as sign_holder_commitment, but exists only for tests to get access to holder commitment
/// transactions which will be broadcasted later, after the channel has moved on to a newer
/// state. Thus, needs its own method as sign_holder_commitment may enforce that we only ever
Expand DownExpand Up@@ -319,13 +332,15 @@ pub trait ChannelKeys : Send+Clone {
/// protocol.
fn sign_channel_announcement<T: secp256k1::Signing>(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Set the counterparty channel basepoints and counterparty_selected/holder_selected_contest_delay.
/// This is done immediately on incoming channels and as soon as the channel is accepted on outgoing channels.
/// Set the counterparty static channel data, including basepoints,
/// counterparty_selected/holder_selected_contest_delay and funding outpoint.
/// This is done as soon as the funding outpoint is known. Since these are static channel data,
/// they MUST NOT be allowed to change to different values once set.
///
/// We bind holder_selected_contest_delay late here for API convenience.
///
/// Will be called before any signatures are applied.
fn on_accept(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16);
fn ready_channel(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint);
}

/// A trait to describe an object which can get user secrets and key material.
Expand All@@ -349,11 +364,11 @@ pub trait KeysInterface: Send + Sync {
}

#[derive(Clone)]
/// Holds late-bound channel data.
/// This data is available after the channel is known to be accepted, either
/// when receiving an open_channel for an inbound channel or when
/// receiving accept_channel for an outbound channel.
struct AcceptedChannelData {
/// Holds late-bound static channel data.
/// This data is available after the channel is readied, either
/// when receiving an funding_created for an inbound channel or when
/// creating a funding transaction for an outbound channel.
struct StaticChannelData {
/// Counterparty public keys and base points
counterparty_channel_pubkeys: ChannelPublicKeys,
/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
Expand All@@ -365,6 +380,8 @@ struct AcceptedChannelData {
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
holder_selected_contest_delay: u16,
/// Whether the holder is the initiator of this channel
Comment thread
devrandom marked this conversation as resolved.
is_outbound: bool,
}

#[derive(Clone)]
Expand All@@ -385,7 +402,9 @@ pub struct InMemoryChannelKeys {
/// Holder public keys and basepoints
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
/// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance
accepted_channel_data: Option<AcceptedChannelData>,
accepted_channel_data: Option<StaticChannelData>,
/// Funding outpoint, populated on channel funding creation
funding_outpoint: Option<OutPoint>,
/// The total value of this channel
channel_value_satoshis: u64,
/// Key derivation parameters
Expand DownExpand Up@@ -418,6 +437,7 @@ impl InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: None,
funding_outpoint: None,
key_derivation_params,
}
}
Expand All@@ -439,21 +459,83 @@ impl InMemoryChannelKeys {
}

/// Counterparty pubkeys.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.accepted_channel_data.as_ref().unwrap().counterparty_channel_pubkeys }

/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
/// transactions, ie the amount of time that we have to wait to recover our funds if we
/// broadcast a transaction. You'll likely want to pass this to the
/// ln::chan_utils::build*_transaction functions when signing holder's transactions.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().counterparty_selected_contest_delay }

/// The contest_delay value specified by us and applied on transactions broadcastable
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn holder_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().holder_selected_contest_delay }

/// Whether the holder is the initiator
pub fn is_outbound(&self) -> bool { self.accepted_channel_data.as_ref().unwrap().is_outbound }

/// Funding outpoint
/// Will panic if ready_channel wasn't called.
pub fn funding_outpoint(&self) -> &OutPoint { self.funding_outpoint.as_ref().unwrap() }

/// Prepare build information for a commitment tx that the holder can broadcast
pub fn build_holder_commitment_tx<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this need to be in the public interface, or are you imaging this is only a utility function in the InMemory version?

&self,
commitment_number: u64,
per_commitment_point: &PublicKey,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
keys: &TxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(bitcoin::Transaction, Vec<HTLCOutputInCommitment>, Vec<Script>), ()> {
let counterparty_points = self.counterparty_pubkeys();

let to_holder_delayed_pubkey = derive_public_key(
&secp_ctx,
&per_commitment_point,
&self.pubkeys().delayed_payment_basepoint,
).map_err(|_| ())?;

let revocation_pubkey = derive_public_revocation_key(
&secp_ctx,
&per_commitment_point,
&counterparty_points.revocation_basepoint,
).map_err(|_| ())?;

let info = CommitmentInfo {
is_counterparty_broadcaster: false,
to_countersigner_pubkey: counterparty_points.payment_point,
to_countersigner_value_sat: to_counterparty_value_sat,
revocation_pubkey,
to_broadcaster_delayed_pubkey: to_holder_delayed_pubkey,
to_broadcaster_value_sat: to_holder_value_sat,
to_self_delay: self.counterparty_selected_contest_delay(),
htlcs: htlcs.clone(),
};

let obscured_commitment_transaction_number =
self.get_commitment_transaction_number_obscure_factor() ^ commitment_number;
let (tx, htlcs, scripts) = build_commitment_tx(
&keys,
&info,
obscured_commitment_transaction_number,
self.funding_outpoint().into_bitcoin_outpoint(),
);
Ok((tx, htlcs, scripts))
}

fn get_commitment_transaction_number_obscure_factor(&self) -> u64 {
get_commitment_transaction_number_obscure_factor(
&self.pubkeys().payment_point,
&self.counterparty_pubkeys().payment_point,
self.is_outbound(),
)
}
}

impl ChannelKeys for InMemoryChannelKeys {
Expand DownExpand Up@@ -507,6 +589,48 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(holder_commitment_tx.get_holder_sig(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
}

fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_number: u64, feerate_per_kw: u32, to_holder_value_sat: u64, to_counterparty_value_sat: u64, htlcs: &Vec<HTLCOutputInCommitment>, pre_keys: &PreCalculatedTxCreationKeys, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()> {
let per_commitment_point = self.get_per_commitment_point(commitment_number, secp_ctx);
let keys = pre_keys.trust_key_derivation();
let (tx, htlcs, _) = self.build_holder_commitment_tx(
commitment_number,
&per_commitment_point,
to_holder_value_sat,
to_counterparty_value_sat,
htlcs,
keys,
secp_ctx,
)?;

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);
let sighash = hash_to_message!(&bip143::SigHashCache::new(&tx)
.signature_hash(0, &funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
let sig = secp_ctx.sign(&sighash, &self.funding_key);

// We provide a dummy signature for the remote, since we don't require that sig
// to be passed in to this call. It would have been better if HolderCommitmentTransaction
// didn't require the remote sig.
let dummy_sig = Secp256k1::new().sign(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This awkwardness is due to HolderCommitmentTransaction holding counterparty signatures. Not sure what's the best thing here - perhaps separate the signature holding to another struct?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would like to get ride of HolderCommitmentTransaction. It's API can be enforced either by ChannelMonitor or an external signer implementation like InMemoryChannelKeys.

All dynamic state (balances, HTLCs, ...) would be stored in Channel and the rest assumed to be in signer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why? HolderCommitmentTransaction, indeed, holds too much data today (specifically, the transaction itself), but communicating things like the counterparty signatures and the per htlc values has to be done. eg here the function signature has a ton of fields, that may be usefully just shoved into a struct, and HolderCommitmentTransaction is basically that. (I assume we agree here, just noting that we may end up wanting a struct either way).

&secp256k1::Message::from_slice(&[42; 32]).unwrap(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
let htlcs_with_sig = htlcs.iter().map(|h| (h.clone(), Some(dummy_sig.clone()))).collect();
let commitment_tx = HolderCommitmentTransaction::new_missing_holder_sig(
tx,
dummy_sig,
&self.pubkeys().funding_pubkey,
&self.counterparty_pubkeys().funding_pubkey,
keys.clone(),
feerate_per_kw,
htlcs_with_sig,
);
let htlc_sigs_o = self.sign_holder_commitment_htlc_transactions(&commitment_tx, secp_ctx)?;
let htlc_sigs = htlc_sigs_o.iter().map(|o| o.unwrap()).collect();

Ok((sig, htlc_sigs))
}

#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
fn unsafe_sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
Expand DownExpand Up@@ -588,18 +712,21 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(secp_ctx.sign(&msghash, &self.funding_key))
}

fn on_accept(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16) {
assert!(self.accepted_channel_data.is_none(), "Already accepted");
self.accepted_channel_data = Some(AcceptedChannelData {
fn ready_channel(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint) {
assert!(self.accepted_channel_data.is_none(), "Acceptance already noted");
self.accepted_channel_data = Some(StaticChannelData {
counterparty_channel_pubkeys: channel_pubkeys.clone(),
counterparty_selected_contest_delay,
holder_selected_contest_delay,
is_outbound,
});
assert!(self.funding_outpoint.is_none(), "Funding creation already noted");
self.funding_outpoint = Some(funding_outpoint.clone());
}
}

impl_writeable!(AcceptedChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay });
impl_writeable!(StaticChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay, is_outbound });

impl Writeable for InMemoryChannelKeys {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
Expand All@@ -610,6 +737,7 @@ impl Writeable for InMemoryChannelKeys {
self.htlc_base_key.write(writer)?;
self.commitment_seed.write(writer)?;
self.accepted_channel_data.write(writer)?;
self.funding_outpoint.write(writer)?;
self.channel_value_satoshis.write(writer)?;
self.key_derivation_params.0.write(writer)?;
self.key_derivation_params.1.write(writer)?;
Expand All@@ -627,6 +755,7 @@ impl Readable for InMemoryChannelKeys {
let htlc_base_key = Readable::read(reader)?;
let commitment_seed = Readable::read(reader)?;
let counterparty_channel_data = Readable::read(reader)?;
let funding_outpoint = Readable::read(reader)?;
let channel_value_satoshis = Readable::read(reader)?;
let secp_ctx = Secp256k1::signing_only();
let holder_channel_pubkeys =
Expand All@@ -646,6 +775,7 @@ impl Readable for InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: counterparty_channel_data,
funding_outpoint,
key_derivation_params: (params_1, params_2),
})
}
Expand Down
16 changes: 15 additions & 1 deletion lightning/src/ln/chan_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -372,7 +372,8 @@ impl_writeable!(ChannelPublicKeys, 33*5, {


impl TxCreationKeys {
/// Create a new TxCreationKeys from channel base points and the per-commitment point
/// Create per-state keys from channel base points and the per-commitment point
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> Result<TxCreationKeys, SecpError> {
Ok(TxCreationKeys {
per_commitment_point: per_commitment_point.clone(),
Expand All@@ -382,6 +383,19 @@ impl TxCreationKeys {
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base)?,
})
}

/// Generate per-state keys from channel static keys.
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn make_tx_keys<T: secp256k1::Signing + secp256k1::Verification>(per_commitment_point: &PublicKey, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TxCreationKeys, SecpError> {
Comment thread
devrandom marked this conversation as resolved.
TxCreationKeys::derive_new(
&secp_ctx,
&per_commitment_point,
&broadcaster_keys.delayed_payment_basepoint,
&broadcaster_keys.htlc_basepoint,
&countersignatory_keys.revocation_basepoint,
&countersignatory_keys.htlc_basepoint,
)
}
}

/// A script either spendable by the revocation
Expand Down
10 changes: 5 additions & 5 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -582,15 +582,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
where K::Target: KeysInterface<ChanKeySigner = ChanSigner>,
F::Target: FeeEstimator
{
let mut chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: msg.funding_pubkey,
revocation_basepoint: msg.revocation_basepoint,
payment_point: msg.payment_point,
delayed_payment_basepoint: msg.delayed_payment_basepoint,
htlc_basepoint: msg.htlc_basepoint
};
chan_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, config.own_channel_config.our_to_self_delay);
let mut local_config = (*config).channel_options.clone();

if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT {
Expand DownExpand Up@@ -1463,7 +1462,6 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
htlc_basepoint: msg.htlc_basepoint
};

self.holder_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, self.holder_selected_contest_delay);
self.counterparty_pubkeys = Some(counterparty_pubkeys);

self.counterparty_cur_commitment_point = Some(msg.first_per_commitment_point);
Expand DownExpand Up@@ -1518,6 +1516,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
}

let funding_txo = OutPoint{ txid: msg.funding_txid, index: msg.funding_output_index };
self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());

let (counterparty_initial_commitment_tx, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
Expand DownExpand Up@@ -3545,6 +3544,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
}

self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());
let signature = match self.get_outbound_funding_created_signature(logger) {
Ok(res) => res,
Expand DownExpand Up@@ -4647,7 +4647,7 @@ mod tests {
chan.holder_dust_limit_satoshis = 546;

let funding_info = OutPoint{ txid: Txid::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), index: 0 };
chan.funding_txo = Some(funding_info);
chan.funding_txo = Some(funding_info.clone());

let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
Expand All@@ -4656,7 +4656,7 @@ mod tests {
delayed_payment_basepoint: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
htlc_basepoint: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444")
};
chan_keys.on_accept(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay);
chan_keys.ready_channel(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay, false, &funding_info);

assert_eq!(counterparty_pubkeys.payment_point.serialize()[..],
hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]);
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ pub mod peer_handler;
pub mod chan_utils;
pub mod features;
pub(crate) mod onchaintx;
pub mod transaction;

#[cfg(feature = "fuzztarget")]
pub mod peer_channel_encryptor;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Draft: phase-2 signer API by devrandom · Pull Request #723 · lightningdevkit/rust-lightning · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 149 additions & 19 deletions lightning/src/chain/keysinterface.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,12 +33,13 @@ use util::ser::{Writeable, Writer, Readable};

use chain::transaction::OutPoint;
use ln::chan_utils;
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys};
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, PreCalculatedTxCreationKeys, derive_public_key, derive_public_revocation_key, TxCreationKeys};
use ln::msgs::UnsignedChannelAnnouncement;

use std::sync::atomic::{AtomicUsize, Ordering};
use std::io::Error;
use ln::msgs::DecodeError;
use ln::transaction::{CommitmentInfo, build_commitment_tx, get_commitment_transaction_number_obscure_factor};

/// 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
Expand DownExpand Up@@ -79,7 +80,7 @@ pub enum SpendableOutputDescriptor {
///
/// To derive the revocation_pubkey provided here (which is used in the witness
/// script generation), you must pass the counterparty revocation_basepoint (which appears in the
/// call to ChannelKeys::on_accept) and the provided per_commitment point
/// call to ChannelKeys::ready_channel) and the provided per_commitment point
/// to chan_utils::derive_public_revocation_key.
///
/// The witness script which is hashed and included in the output script_pubkey may be
Expand DownExpand Up@@ -244,6 +245,18 @@ pub trait ChannelKeys : Send+Clone {
// TODO: Add more input vars to enable better checking (preferably removing commitment_tx and
fn sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Create a signature for a holder's commitment transaction and attached HTLC transactions.
fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replaces the previous function.

&self,
commitment_number: u64,
feerate_per_kw: u32,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
pre_keys: &PreCalculatedTxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(Signature, Vec<Signature>), ()>;

/// Same as sign_holder_commitment, but exists only for tests to get access to holder commitment
/// transactions which will be broadcasted later, after the channel has moved on to a newer
/// state. Thus, needs its own method as sign_holder_commitment may enforce that we only ever
Expand DownExpand Up@@ -319,13 +332,15 @@ pub trait ChannelKeys : Send+Clone {
/// protocol.
fn sign_channel_announcement<T: secp256k1::Signing>(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()>;

/// Set the counterparty channel basepoints and counterparty_selected/holder_selected_contest_delay.
/// This is done immediately on incoming channels and as soon as the channel is accepted on outgoing channels.
/// Set the counterparty static channel data, including basepoints,
/// counterparty_selected/holder_selected_contest_delay and funding outpoint.
/// This is done as soon as the funding outpoint is known. Since these are static channel data,
/// they MUST NOT be allowed to change to different values once set.
///
/// We bind holder_selected_contest_delay late here for API convenience.
///
/// Will be called before any signatures are applied.
fn on_accept(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16);
fn ready_channel(&mut self, channel_points: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint);
}

/// A trait to describe an object which can get user secrets and key material.
Expand All@@ -349,11 +364,11 @@ pub trait KeysInterface: Send + Sync {
}

#[derive(Clone)]
/// Holds late-bound channel data.
/// This data is available after the channel is known to be accepted, either
/// when receiving an open_channel for an inbound channel or when
/// receiving accept_channel for an outbound channel.
struct AcceptedChannelData {
/// Holds late-bound static channel data.
/// This data is available after the channel is readied, either
/// when receiving an funding_created for an inbound channel or when
/// creating a funding transaction for an outbound channel.
struct StaticChannelData {
/// Counterparty public keys and base points
counterparty_channel_pubkeys: ChannelPublicKeys,
/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
Expand All@@ -365,6 +380,8 @@ struct AcceptedChannelData {
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
holder_selected_contest_delay: u16,
/// Whether the holder is the initiator of this channel
Comment thread
devrandom marked this conversation as resolved.
is_outbound: bool,
}

#[derive(Clone)]
Expand All@@ -385,7 +402,9 @@ pub struct InMemoryChannelKeys {
/// Holder public keys and basepoints
pub(crate) holder_channel_pubkeys: ChannelPublicKeys,
/// Counterparty public keys and counterparty/holder selected_contest_delay, populated on channel acceptance
accepted_channel_data: Option<AcceptedChannelData>,
accepted_channel_data: Option<StaticChannelData>,
/// Funding outpoint, populated on channel funding creation
funding_outpoint: Option<OutPoint>,
/// The total value of this channel
channel_value_satoshis: u64,
/// Key derivation parameters
Expand DownExpand Up@@ -418,6 +437,7 @@ impl InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: None,
funding_outpoint: None,
key_derivation_params,
}
}
Expand All@@ -439,21 +459,83 @@ impl InMemoryChannelKeys {
}

/// Counterparty pubkeys.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.accepted_channel_data.as_ref().unwrap().counterparty_channel_pubkeys }

/// The contest_delay value specified by our counterparty and applied on holder-broadcastable
/// transactions, ie the amount of time that we have to wait to recover our funds if we
/// broadcast a transaction. You'll likely want to pass this to the
/// ln::chan_utils::build*_transaction functions when signing holder's transactions.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn counterparty_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().counterparty_selected_contest_delay }

/// The contest_delay value specified by us and applied on transactions broadcastable
/// by our counterparty, ie the amount of time that they have to wait to recover their funds
/// if they broadcast a transaction.
/// Will panic if on_accept wasn't called.
/// Will panic if ready_channel wasn't called.
pub fn holder_selected_contest_delay(&self) -> u16 { self.accepted_channel_data.as_ref().unwrap().holder_selected_contest_delay }

/// Whether the holder is the initiator
pub fn is_outbound(&self) -> bool { self.accepted_channel_data.as_ref().unwrap().is_outbound }

/// Funding outpoint
/// Will panic if ready_channel wasn't called.
pub fn funding_outpoint(&self) -> &OutPoint { self.funding_outpoint.as_ref().unwrap() }

/// Prepare build information for a commitment tx that the holder can broadcast
pub fn build_holder_commitment_tx<T: secp256k1::Signing + secp256k1::Verification>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this need to be in the public interface, or are you imaging this is only a utility function in the InMemory version?

&self,
commitment_number: u64,
per_commitment_point: &PublicKey,
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
htlcs: &Vec<HTLCOutputInCommitment>,
keys: &TxCreationKeys,
secp_ctx: &Secp256k1<T>,
) -> Result<(bitcoin::Transaction, Vec<HTLCOutputInCommitment>, Vec<Script>), ()> {
let counterparty_points = self.counterparty_pubkeys();

let to_holder_delayed_pubkey = derive_public_key(
&secp_ctx,
&per_commitment_point,
&self.pubkeys().delayed_payment_basepoint,
).map_err(|_| ())?;

let revocation_pubkey = derive_public_revocation_key(
&secp_ctx,
&per_commitment_point,
&counterparty_points.revocation_basepoint,
).map_err(|_| ())?;

let info = CommitmentInfo {
is_counterparty_broadcaster: false,
to_countersigner_pubkey: counterparty_points.payment_point,
to_countersigner_value_sat: to_counterparty_value_sat,
revocation_pubkey,
to_broadcaster_delayed_pubkey: to_holder_delayed_pubkey,
to_broadcaster_value_sat: to_holder_value_sat,
to_self_delay: self.counterparty_selected_contest_delay(),
htlcs: htlcs.clone(),
};

let obscured_commitment_transaction_number =
self.get_commitment_transaction_number_obscure_factor() ^ commitment_number;
let (tx, htlcs, scripts) = build_commitment_tx(
&keys,
&info,
obscured_commitment_transaction_number,
self.funding_outpoint().into_bitcoin_outpoint(),
);
Ok((tx, htlcs, scripts))
}

fn get_commitment_transaction_number_obscure_factor(&self) -> u64 {
get_commitment_transaction_number_obscure_factor(
&self.pubkeys().payment_point,
&self.counterparty_pubkeys().payment_point,
self.is_outbound(),
)
}
}

impl ChannelKeys for InMemoryChannelKeys {
Expand DownExpand Up@@ -507,6 +589,48 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(holder_commitment_tx.get_holder_sig(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
}

fn sign_holder_commitment_phase2<T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_number: u64, feerate_per_kw: u32, to_holder_value_sat: u64, to_counterparty_value_sat: u64, htlcs: &Vec<HTLCOutputInCommitment>, pre_keys: &PreCalculatedTxCreationKeys, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()> {
let per_commitment_point = self.get_per_commitment_point(commitment_number, secp_ctx);
let keys = pre_keys.trust_key_derivation();
let (tx, htlcs, _) = self.build_holder_commitment_tx(
commitment_number,
&per_commitment_point,
to_holder_value_sat,
to_counterparty_value_sat,
htlcs,
keys,
secp_ctx,
)?;

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);
let sighash = hash_to_message!(&bip143::SigHashCache::new(&tx)
.signature_hash(0, &funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
let sig = secp_ctx.sign(&sighash, &self.funding_key);

// We provide a dummy signature for the remote, since we don't require that sig
// to be passed in to this call. It would have been better if HolderCommitmentTransaction
// didn't require the remote sig.
let dummy_sig = Secp256k1::new().sign(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This awkwardness is due to HolderCommitmentTransaction holding counterparty signatures. Not sure what's the best thing here - perhaps separate the signature holding to another struct?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would like to get ride of HolderCommitmentTransaction. It's API can be enforced either by ChannelMonitor or an external signer implementation like InMemoryChannelKeys.

All dynamic state (balances, HTLCs, ...) would be stored in Channel and the rest assumed to be in signer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why? HolderCommitmentTransaction, indeed, holds too much data today (specifically, the transaction itself), but communicating things like the counterparty signatures and the per htlc values has to be done. eg here the function signature has a ton of fields, that may be usefully just shoved into a struct, and HolderCommitmentTransaction is basically that. (I assume we agree here, just noting that we may end up wanting a struct either way).

&secp256k1::Message::from_slice(&[42; 32]).unwrap(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
let htlcs_with_sig = htlcs.iter().map(|h| (h.clone(), Some(dummy_sig.clone()))).collect();
let commitment_tx = HolderCommitmentTransaction::new_missing_holder_sig(
tx,
dummy_sig,
&self.pubkeys().funding_pubkey,
&self.counterparty_pubkeys().funding_pubkey,
keys.clone(),
feerate_per_kw,
htlcs_with_sig,
);
let htlc_sigs_o = self.sign_holder_commitment_htlc_transactions(&commitment_tx, secp_ctx)?;
let htlc_sigs = htlc_sigs_o.iter().map(|o| o.unwrap()).collect();

Ok((sig, htlc_sigs))
}

#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
fn unsafe_sign_holder_commitment<T: secp256k1::Signing + secp256k1::Verification>(&self, holder_commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<T>) -> Result<Signature, ()> {
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
Expand DownExpand Up@@ -588,18 +712,21 @@ impl ChannelKeys for InMemoryChannelKeys {
Ok(secp_ctx.sign(&msghash, &self.funding_key))
}

fn on_accept(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16) {
assert!(self.accepted_channel_data.is_none(), "Already accepted");
self.accepted_channel_data = Some(AcceptedChannelData {
fn ready_channel(&mut self, channel_pubkeys: &ChannelPublicKeys, counterparty_selected_contest_delay: u16, holder_selected_contest_delay: u16, is_outbound: bool, funding_outpoint: &OutPoint) {
assert!(self.accepted_channel_data.is_none(), "Acceptance already noted");
self.accepted_channel_data = Some(StaticChannelData {
counterparty_channel_pubkeys: channel_pubkeys.clone(),
counterparty_selected_contest_delay,
holder_selected_contest_delay,
is_outbound,
});
assert!(self.funding_outpoint.is_none(), "Funding creation already noted");
self.funding_outpoint = Some(funding_outpoint.clone());
}
}

impl_writeable!(AcceptedChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay });
impl_writeable!(StaticChannelData, 0,
{ counterparty_channel_pubkeys, counterparty_selected_contest_delay, holder_selected_contest_delay, is_outbound });

impl Writeable for InMemoryChannelKeys {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
Expand All@@ -610,6 +737,7 @@ impl Writeable for InMemoryChannelKeys {
self.htlc_base_key.write(writer)?;
self.commitment_seed.write(writer)?;
self.accepted_channel_data.write(writer)?;
self.funding_outpoint.write(writer)?;
self.channel_value_satoshis.write(writer)?;
self.key_derivation_params.0.write(writer)?;
self.key_derivation_params.1.write(writer)?;
Expand All@@ -627,6 +755,7 @@ impl Readable for InMemoryChannelKeys {
let htlc_base_key = Readable::read(reader)?;
let commitment_seed = Readable::read(reader)?;
let counterparty_channel_data = Readable::read(reader)?;
let funding_outpoint = Readable::read(reader)?;
let channel_value_satoshis = Readable::read(reader)?;
let secp_ctx = Secp256k1::signing_only();
let holder_channel_pubkeys =
Expand All@@ -646,6 +775,7 @@ impl Readable for InMemoryChannelKeys {
channel_value_satoshis,
holder_channel_pubkeys,
accepted_channel_data: counterparty_channel_data,
funding_outpoint,
key_derivation_params: (params_1, params_2),
})
}
Expand Down
16 changes: 15 additions & 1 deletion lightning/src/ln/chan_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -372,7 +372,8 @@ impl_writeable!(ChannelPublicKeys, 33*5, {


impl TxCreationKeys {
/// Create a new TxCreationKeys from channel base points and the per-commitment point
/// Create per-state keys from channel base points and the per-commitment point
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> Result<TxCreationKeys, SecpError> {
Ok(TxCreationKeys {
per_commitment_point: per_commitment_point.clone(),
Expand All@@ -382,6 +383,19 @@ impl TxCreationKeys {
broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base)?,
})
}

/// Generate per-state keys from channel static keys.
/// Key set is asymmetric and can't be used as part of counter-signatory set of transactions.
pub fn make_tx_keys<T: secp256k1::Signing + secp256k1::Verification>(per_commitment_point: &PublicKey, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TxCreationKeys, SecpError> {
Comment thread
devrandom marked this conversation as resolved.
TxCreationKeys::derive_new(
&secp_ctx,
&per_commitment_point,
&broadcaster_keys.delayed_payment_basepoint,
&broadcaster_keys.htlc_basepoint,
&countersignatory_keys.revocation_basepoint,
&countersignatory_keys.htlc_basepoint,
)
}
}

/// A script either spendable by the revocation
Expand Down
10 changes: 5 additions & 5 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -582,15 +582,14 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
where K::Target: KeysInterface<ChanKeySigner = ChanSigner>,
F::Target: FeeEstimator
{
let mut chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis);
let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: msg.funding_pubkey,
revocation_basepoint: msg.revocation_basepoint,
payment_point: msg.payment_point,
delayed_payment_basepoint: msg.delayed_payment_basepoint,
htlc_basepoint: msg.htlc_basepoint
};
chan_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, config.own_channel_config.our_to_self_delay);
let mut local_config = (*config).channel_options.clone();

if config.own_channel_config.our_to_self_delay < BREAKDOWN_TIMEOUT {
Expand DownExpand Up@@ -1463,7 +1462,6 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
htlc_basepoint: msg.htlc_basepoint
};

self.holder_keys.on_accept(&counterparty_pubkeys, msg.to_self_delay, self.holder_selected_contest_delay);
self.counterparty_pubkeys = Some(counterparty_pubkeys);

self.counterparty_cur_commitment_point = Some(msg.first_per_commitment_point);
Expand DownExpand Up@@ -1518,6 +1516,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
}

let funding_txo = OutPoint{ txid: msg.funding_txid, index: msg.funding_output_index };
self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());

let (counterparty_initial_commitment_tx, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
Expand DownExpand Up@@ -3545,6 +3544,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
}

self.holder_keys.ready_channel(self.counterparty_pubkeys.as_ref().unwrap(), self.counterparty_selected_contest_delay, self.holder_selected_contest_delay, self.channel_outbound, &funding_txo);
self.funding_txo = Some(funding_txo.clone());
let signature = match self.get_outbound_funding_created_signature(logger) {
Ok(res) => res,
Expand DownExpand Up@@ -4647,7 +4647,7 @@ mod tests {
chan.holder_dust_limit_satoshis = 546;

let funding_info = OutPoint{ txid: Txid::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), index: 0 };
chan.funding_txo = Some(funding_info);
chan.funding_txo = Some(funding_info.clone());

let counterparty_pubkeys = ChannelPublicKeys {
funding_pubkey: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
Expand All@@ -4656,7 +4656,7 @@ mod tests {
delayed_payment_basepoint: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"),
htlc_basepoint: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444")
};
chan_keys.on_accept(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay);
chan_keys.ready_channel(&counterparty_pubkeys, chan.counterparty_selected_contest_delay, chan.holder_selected_contest_delay, false, &funding_info);

assert_eq!(counterparty_pubkeys.payment_point.serialize()[..],
hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]);
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@ pub mod peer_handler;
pub mod chan_utils;
pub mod features;
pub(crate) mod onchaintx;
pub mod transaction;

#[cfg(feature = "fuzztarget")]
pub mod peer_channel_encryptor;
Expand Down
Loading