Skip to content

Commit 9310813

Browse files
authored
Merge pull request #420 from TheBlueMatt/2019-12-chan-ext-signer
Remove signing from Channel
2 parents f755ae5 + e2e1628 commit 9310813

File tree

7 files changed

+477
-422
lines changed

7 files changed

+477
-422
lines changed

fuzz/src/chanmon_consistency.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ pub fn do_test(data: &[u8]) {
212212
monitor.latest_good_update.lock().unwrap().insert(outpoint, monitor_ser);
213213
}
214214
let mut monitor_refs = HashMap::new();
215-
for (outpoint, monitor) in monitors.iter() {
215+
for (outpoint, monitor) in monitors.iter_mut() {
216216
monitor_refs.insert(*outpoint, monitor);
217217
}
218218

@@ -223,7 +223,7 @@ pub fn do_test(data: &[u8]) {
223223
tx_broadcaster: broadcast.clone(),
224224
logger,
225225
default_config: config,
226-
channel_monitors: &monitor_refs,
226+
channel_monitors: &mut monitor_refs,
227227
};
228228

229229
let res = (<(Sha256d, ChannelManager<EnforcingChannelKeys>)>::read(&mut Cursor::new(&$ser.0), read_args).expect("Failed to read manager").1, monitor);

lightning/src/chain/keysinterface.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ pub trait ChannelKeys : Send {
142142
/// TODO: Document the things someone using this interface should enforce before signing.
143143
/// TODO: Add more input vars to enable better checking (preferably removing commitment_tx and
144144
/// making the callee generate it via some util function we expose)!
145-
fn sign_remote_commitment<T: secp256k1::Signing>(&self, channel_value_satoshis: u64, channel_funding_redeemscript: &Script, absolute_fee: u64, commitment_tx: &Transaction, keys: &TxCreationKeys, htlcs: &[&HTLCOutputInCommitment], to_self_delay: u16, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()>;
145+
fn sign_remote_commitment<T: secp256k1::Signing>(&self, channel_value_satoshis: u64, channel_funding_redeemscript: &Script, feerate_per_kw: u64, commitment_tx: &Transaction, keys: &TxCreationKeys, htlcs: &[&HTLCOutputInCommitment], to_self_delay: u16, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Vec<Signature>), ()>;
146146

147147
/// Create a signature for a (proposed) closing transaction.
148148
///

lightning/src/ln/chan_utils.rs

+161-5
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,22 @@
44
55
use bitcoin::blockdata::script::{Script,Builder};
66
use bitcoin::blockdata::opcodes;
7-
use bitcoin::blockdata::transaction::{TxIn,TxOut,OutPoint,Transaction};
7+
use bitcoin::blockdata::transaction::{TxIn,TxOut,OutPoint,Transaction, SigHashType};
8+
use bitcoin::consensus::encode::{self, Decodable, Encodable};
9+
use bitcoin::util::bip143;
810

911
use bitcoin_hashes::{Hash, HashEngine};
1012
use bitcoin_hashes::sha256::Hash as Sha256;
1113
use bitcoin_hashes::ripemd160::Hash as Ripemd160;
1214
use bitcoin_hashes::hash160::Hash as Hash160;
1315
use bitcoin_hashes::sha256d::Hash as Sha256dHash;
1416

15-
use ln::channelmanager::PaymentHash;
17+
use ln::channelmanager::{PaymentHash, PaymentPreimage};
18+
use ln::msgs::DecodeError;
19+
use util::ser::{Readable, Writeable, Writer, WriterWriteAdaptor};
1620

17-
use secp256k1::key::{PublicKey,SecretKey};
18-
use secp256k1::Secp256k1;
21+
use secp256k1::key::{SecretKey,PublicKey};
22+
use secp256k1::{Secp256k1, Signature};
1923
use secp256k1;
2024

2125
pub(super) const HTLC_SUCCESS_TX_WEIGHT: u64 = 703;
@@ -59,7 +63,9 @@ pub(super) fn derive_public_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>,
5963
base_point.combine(&hashkey)
6064
}
6165

62-
/// Derives a revocation key from its constituent parts
66+
/// Derives a revocation key from its constituent parts.
67+
/// Note that this is infallible iff we trust that at least one of the two input keys are randomly
68+
/// generated (ie our own).
6369
pub(super) fn derive_private_revocation_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, per_commitment_secret: &SecretKey, revocation_base_secret: &SecretKey) -> Result<SecretKey, secp256k1::Error> {
6470
let revocation_base_point = PublicKey::from_secret_key(&secp_ctx, &revocation_base_secret);
6571
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
@@ -281,3 +287,153 @@ pub fn build_htlc_transaction(prev_hash: &Sha256dHash, feerate_per_kw: u64, to_s
281287
output: txouts,
282288
}
283289
}
290+
291+
/// Signs a transaction created by build_htlc_transaction. If the transaction is an
292+
/// HTLC-Success transaction (ie htlc.offered is false), preimage must be set!
293+
pub(crate) fn sign_htlc_transaction<T: secp256k1::Signing>(tx: &mut Transaction, their_sig: &Signature, preimage: &Option<PaymentPreimage>, htlc: &HTLCOutputInCommitment, a_htlc_key: &PublicKey, b_htlc_key: &PublicKey, revocation_key: &PublicKey, per_commitment_point: &PublicKey, htlc_base_key: &SecretKey, secp_ctx: &Secp256k1<T>) -> Result<(Signature, Script), ()> {
294+
if tx.input.len() != 1 { return Err(()); }
295+
if tx.input[0].witness.len() != 0 { return Err(()); }
296+
297+
let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&htlc, a_htlc_key, b_htlc_key, revocation_key);
298+
299+
let our_htlc_key = derive_private_key(secp_ctx, per_commitment_point, htlc_base_key).map_err(|_| ())?;
300+
let sighash = hash_to_message!(&bip143::SighashComponents::new(&tx).sighash_all(&tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]);
301+
let local_tx = PublicKey::from_secret_key(&secp_ctx, &our_htlc_key) == *a_htlc_key;
302+
let our_sig = secp_ctx.sign(&sighash, &our_htlc_key);
303+
304+
tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
305+
306+
if local_tx { // b, then a
307+
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
308+
tx.input[0].witness.push(our_sig.serialize_der().to_vec());
309+
} else {
310+
tx.input[0].witness.push(our_sig.serialize_der().to_vec());
311+
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
312+
}
313+
tx.input[0].witness[1].push(SigHashType::All as u8);
314+
tx.input[0].witness[2].push(SigHashType::All as u8);
315+
316+
if htlc.offered {
317+
tx.input[0].witness.push(Vec::new());
318+
assert!(preimage.is_none());
319+
} else {
320+
tx.input[0].witness.push(preimage.unwrap().0.to_vec());
321+
}
322+
323+
tx.input[0].witness.push(htlc_redeemscript.as_bytes().to_vec());
324+
325+
Ok((our_sig, htlc_redeemscript))
326+
}
327+
328+
#[derive(Clone)]
329+
/// We use this to track local commitment transactions and put off signing them until we are ready
330+
/// to broadcast. Eventually this will require a signer which is possibly external, but for now we
331+
/// just pass in the SecretKeys required.
332+
pub(crate) struct LocalCommitmentTransaction {
333+
tx: Transaction
334+
}
335+
impl LocalCommitmentTransaction {
336+
#[cfg(test)]
337+
pub fn dummy() -> Self {
338+
Self { tx: Transaction {
339+
version: 2,
340+
input: Vec::new(),
341+
output: Vec::new(),
342+
lock_time: 0,
343+
} }
344+
}
345+
346+
pub fn new_missing_local_sig(mut tx: Transaction, their_sig: &Signature, our_funding_key: &PublicKey, their_funding_key: &PublicKey) -> LocalCommitmentTransaction {
347+
if tx.input.len() != 1 { panic!("Tried to store a commitment transaction that had input count != 1!"); }
348+
if tx.input[0].witness.len() != 0 { panic!("Tried to store a signed commitment transaction?"); }
349+
350+
tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
351+
352+
if our_funding_key.serialize()[..] < their_funding_key.serialize()[..] {
353+
tx.input[0].witness.push(Vec::new());
354+
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
355+
tx.input[0].witness[2].push(SigHashType::All as u8);
356+
} else {
357+
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
358+
tx.input[0].witness[1].push(SigHashType::All as u8);
359+
tx.input[0].witness.push(Vec::new());
360+
}
361+
362+
Self { tx }
363+
}
364+
365+
pub fn txid(&self) -> Sha256dHash {
366+
self.tx.txid()
367+
}
368+
369+
pub fn has_local_sig(&self) -> bool {
370+
if self.tx.input.len() != 1 { panic!("Commitment transactions must have input count == 1!"); }
371+
if self.tx.input[0].witness.len() == 4 {
372+
assert!(!self.tx.input[0].witness[1].is_empty());
373+
assert!(!self.tx.input[0].witness[2].is_empty());
374+
true
375+
} else {
376+
assert_eq!(self.tx.input[0].witness.len(), 3);
377+
assert!(self.tx.input[0].witness[0].is_empty());
378+
assert!(self.tx.input[0].witness[1].is_empty() || self.tx.input[0].witness[2].is_empty());
379+
false
380+
}
381+
}
382+
383+
pub fn add_local_sig<T: secp256k1::Signing>(&mut self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) {
384+
if self.has_local_sig() { return; }
385+
let sighash = hash_to_message!(&bip143::SighashComponents::new(&self.tx)
386+
.sighash_all(&self.tx.input[0], funding_redeemscript, channel_value_satoshis)[..]);
387+
let our_sig = secp_ctx.sign(&sighash, funding_key);
388+
389+
if self.tx.input[0].witness[1].is_empty() {
390+
self.tx.input[0].witness[1] = our_sig.serialize_der().to_vec();
391+
self.tx.input[0].witness[1].push(SigHashType::All as u8);
392+
} else {
393+
self.tx.input[0].witness[2] = our_sig.serialize_der().to_vec();
394+
self.tx.input[0].witness[2].push(SigHashType::All as u8);
395+
}
396+
397+
self.tx.input[0].witness.push(funding_redeemscript.as_bytes().to_vec());
398+
}
399+
400+
pub fn without_valid_witness(&self) -> &Transaction { &self.tx }
401+
pub fn with_valid_witness(&self) -> &Transaction {
402+
assert!(self.has_local_sig());
403+
&self.tx
404+
}
405+
}
406+
impl PartialEq for LocalCommitmentTransaction {
407+
// We dont care whether we are signed in equality comparison
408+
fn eq(&self, o: &Self) -> bool {
409+
self.txid() == o.txid()
410+
}
411+
}
412+
impl Writeable for LocalCommitmentTransaction {
413+
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
414+
if let Err(e) = self.tx.consensus_encode(&mut WriterWriteAdaptor(writer)) {
415+
match e {
416+
encode::Error::Io(e) => return Err(e),
417+
_ => panic!("local tx must have been well-formed!"),
418+
}
419+
}
420+
Ok(())
421+
}
422+
}
423+
impl<R: ::std::io::Read> Readable<R> for LocalCommitmentTransaction {
424+
fn read(reader: &mut R) -> Result<Self, DecodeError> {
425+
let tx = match Transaction::consensus_decode(reader.by_ref()) {
426+
Ok(tx) => tx,
427+
Err(e) => match e {
428+
encode::Error::Io(ioe) => return Err(DecodeError::Io(ioe)),
429+
_ => return Err(DecodeError::InvalidValue),
430+
},
431+
};
432+
433+
if tx.input.len() != 1 {
434+
// Ensure tx didn't hit the 0-input ambiguity case.
435+
return Err(DecodeError::InvalidValue);
436+
}
437+
Ok(Self { tx })
438+
}
439+
}

0 commit comments

Comments
 (0)