Skip to content

Implement a "phase 2" API for sign_closing_transaction #1064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
15 changes: 4 additions & 11 deletions lightning/src/chain/keysinterface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use util::ser::{Writeable, Writer, Readable};

use chain::transaction::OutPoint;
use ln::chan_utils;
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction};
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction};
use ln::msgs::UnsignedChannelAnnouncement;
use ln::script::ShutdownScript;

Expand Down Expand Up @@ -322,7 +322,7 @@ pub trait BaseSign {
///
/// Note that, due to rounding, there may be one "missing" satoshi, and either party may have
/// chosen to forgo their output as dust.
fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;

/// Signs a channel announcement message with our funding key, proving it comes from one
/// of the channel participants.
Expand Down Expand Up @@ -671,17 +671,10 @@ impl BaseSign for InMemorySigner {
Err(())
}

fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
if closing_tx.input.len() != 1 { return Err(()); }
if closing_tx.input[0].witness.len() != 0 { return Err(()); }
if closing_tx.output.len() > 2 { return Err(()); }

fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);

let sighash = hash_to_message!(&bip143::SigHashCache::new(closing_tx)
.signature_hash(0, &channel_funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
Ok(secp_ctx.sign(&sighash, &self.funding_key))
Ok(closing_tx.trust().sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
}

fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
Expand Down
173 changes: 170 additions & 3 deletions lightning/src/ln/chan_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use bitcoin::hash_types::{Txid, PubkeyHash};
use ln::{PaymentHash, PaymentPreimage};
use ln::msgs::DecodeError;
use util::ser::{Readable, Writeable, Writer};
use util::byte_utils;
use util::{byte_utils, transaction_utils};

use bitcoin::hash_types::WPubkeyHash;
use bitcoin::secp256k1::key::{SecretKey, PublicKey};
Expand Down Expand Up @@ -80,6 +80,50 @@ pub fn build_commitment_secret(commitment_seed: &[u8; 32], idx: u64) -> [u8; 32]
res
}

/// Build a closing transaction
pub fn build_closing_transaction(to_holder_value_sat: u64, to_counterparty_value_sat: u64, to_holder_script: Script, to_counterparty_script: Script, funding_outpoint: OutPoint) -> Transaction {
let txins = {
let mut ins: Vec<TxIn> = Vec::new();
ins.push(TxIn {
previous_output: funding_outpoint,
script_sig: Script::new(),
sequence: 0xffffffff,
witness: Vec::new(),
});
ins
};

let mut txouts: Vec<(TxOut, ())> = Vec::new();

if to_counterparty_value_sat > 0 {
txouts.push((TxOut {
script_pubkey: to_counterparty_script,
value: to_counterparty_value_sat
}, ()));
}

if to_holder_value_sat > 0 {
txouts.push((TxOut {
script_pubkey: to_holder_script,
value: to_holder_value_sat
}, ()));
}

transaction_utils::sort_outputs(&mut txouts, |_, _| { cmp::Ordering::Equal }); // Ordering doesnt matter if they used our pubkey...

let mut outputs: Vec<TxOut> = Vec::new();
for out in txouts.drain(..) {
outputs.push(out.0);
}

Transaction {
version: 2,
lock_time: 0,
input: txins,
output: outputs,
}
}

/// Implements the per-commitment secret storage scheme from
/// [BOLT 3](https://github.com/lightningnetwork/lightning-rfc/blob/dcbf8583976df087c79c3ce0b535311212e6812d/03-transactions.md#efficient-per-commitment-secret-storage).
///
Expand Down Expand Up @@ -846,7 +890,130 @@ impl BuiltCommitmentTransaction {
}
}

/// This class tracks the per-transaction information needed to build a commitment transaction and to
/// This class tracks the per-transaction information needed to build a closing transaction and will
/// actually build it and sign.
///
/// This class can be used inside a signer implementation to generate a signature given the relevant
/// secret key.
pub struct ClosingTransaction {
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
to_holder_script: Script,
to_counterparty_script: Script,
built: Transaction,
}

impl ClosingTransaction {
/// Construct an object of the class
pub fn new(
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
to_holder_script: Script,
to_counterparty_script: Script,
funding_outpoint: OutPoint,
) -> Self {
let built = build_closing_transaction(
to_holder_value_sat, to_counterparty_value_sat,
to_holder_script.clone(), to_counterparty_script.clone(),
funding_outpoint
);
ClosingTransaction {
to_holder_value_sat,
to_counterparty_value_sat,
to_holder_script,
to_counterparty_script,
built
}
}

/// Trust our pre-built transaction.
///
/// Applies a wrapper which allows access to the transaction.
///
/// This should only be used if you fully trust the builder of this object. It should not
/// be used by an external signer - instead use the verify function.
pub fn trust(&self) -> TrustedClosingTransaction {
TrustedClosingTransaction { inner: self }
}

/// Verify our pre-built transaction.
///
/// Applies a wrapper which allows access to the transaction.
///
/// An external validating signer must call this method before signing
/// or using the built transaction.
pub fn verify(&self, funding_outpoint: OutPoint) -> Result<TrustedClosingTransaction, ()> {
let built = build_closing_transaction(
self.to_holder_value_sat, self.to_counterparty_value_sat,
self.to_holder_script.clone(), self.to_counterparty_script.clone(),
funding_outpoint
);
if self.built != built {
return Err(())
}
Ok(TrustedClosingTransaction { inner: self })
}

/// The value to be sent to the holder, or zero if the output will be omitted
pub fn to_holder_value_sat(&self) -> u64 {
self.to_holder_value_sat
}

/// The value to be sent to the counterparty, or zero if the output will be omitted
pub fn to_counterparty_value_sat(&self) -> u64 {
self.to_counterparty_value_sat
}

/// The destination of the holder's output
pub fn to_holder_script(&self) -> &Script {
&self.to_holder_script
}

/// The destination of the counterparty's output
pub fn to_counterparty_script(&self) -> &Script {
&self.to_counterparty_script
}
}

/// A wrapper on ClosingTransaction indicating that the built bitcoin
/// transaction is trusted.
///
/// See trust() and verify() functions on CommitmentTransaction.
///
/// This structure implements Deref.
pub struct TrustedClosingTransaction<'a> {
inner: &'a ClosingTransaction,
}

impl<'a> Deref for TrustedClosingTransaction<'a> {
type Target = ClosingTransaction;

fn deref(&self) -> &Self::Target { self.inner }
}

impl<'a> TrustedClosingTransaction<'a> {
/// The pre-built Bitcoin commitment transaction
pub fn built_transaction(&self) -> &Transaction {
&self.inner.built
}

/// Get the SIGHASH_ALL sighash value of the transaction.
///
/// This can be used to verify a signature.
pub fn get_sighash_all(&self, funding_redeemscript: &Script, channel_value_satoshis: u64) -> Message {
let sighash = &bip143::SigHashCache::new(&self.inner.built).signature_hash(0, funding_redeemscript, channel_value_satoshis, SigHashType::All)[..];
hash_to_message!(sighash)
}

/// Sign a transaction, either because we are counter-signing the counterparty's transaction or
/// because we are about to broadcast a holder transaction.
pub fn sign<T: secp256k1::Signing>(&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) -> Signature {
let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis);
secp_ctx.sign(&sighash, funding_key)
}
}

/// This class tracks the per-transaction information needed to build a commitment transaction and will
/// actually build it and sign. It is used for holder transactions that we sign only when needed
/// and for transactions we sign for the counterparty.
///
Expand Down Expand Up @@ -1110,7 +1277,7 @@ impl CommitmentTransaction {
/// Applies a wrapper which allows access to these fields.
///
/// This should only be used if you fully trust the builder of this object. It should not
/// be used by an external signer - instead use the verify function.
/// be used by an external signer - instead use the verify function.
pub fn trust(&self) -> TrustedCommitmentTransaction {
TrustedCommitmentTransaction { inner: self }
}
Expand Down
Loading