mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 07:17:40 +01:00
Sign local HTLC transactions at broadcast-time, instead of generate
This commit is contained in:
parent
eb97a7534d
commit
e2e1628680
3 changed files with 113 additions and 168 deletions
|
@ -14,7 +14,7 @@ use bitcoin_hashes::ripemd160::Hash as Ripemd160;
|
||||||
use bitcoin_hashes::hash160::Hash as Hash160;
|
use bitcoin_hashes::hash160::Hash as Hash160;
|
||||||
use bitcoin_hashes::sha256d::Hash as Sha256dHash;
|
use bitcoin_hashes::sha256d::Hash as Sha256dHash;
|
||||||
|
|
||||||
use ln::channelmanager::PaymentHash;
|
use ln::channelmanager::{PaymentHash, PaymentPreimage};
|
||||||
use ln::msgs::DecodeError;
|
use ln::msgs::DecodeError;
|
||||||
use util::ser::{Readable, Writeable, Writer, WriterWriteAdaptor};
|
use util::ser::{Readable, Writeable, Writer, WriterWriteAdaptor};
|
||||||
|
|
||||||
|
@ -63,7 +63,9 @@ pub(super) fn derive_public_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>,
|
||||||
base_point.combine(&hashkey)
|
base_point.combine(&hashkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derives a revocation key from its constituent parts
|
/// Derives a revocation key from its constituent parts.
|
||||||
|
/// Note that this is infallible iff we trust that at least one of the two input keys are randomly
|
||||||
|
/// generated (ie our own).
|
||||||
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> {
|
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> {
|
||||||
let revocation_base_point = PublicKey::from_secret_key(&secp_ctx, &revocation_base_secret);
|
let revocation_base_point = PublicKey::from_secret_key(&secp_ctx, &revocation_base_secret);
|
||||||
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
|
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
|
||||||
|
@ -286,6 +288,43 @@ pub fn build_htlc_transaction(prev_hash: &Sha256dHash, feerate_per_kw: u64, to_s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Signs a transaction created by build_htlc_transaction. If the transaction is an
|
||||||
|
/// HTLC-Success transaction (ie htlc.offered is false), preimage must be set!
|
||||||
|
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), ()> {
|
||||||
|
if tx.input.len() != 1 { return Err(()); }
|
||||||
|
if tx.input[0].witness.len() != 0 { return Err(()); }
|
||||||
|
|
||||||
|
let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&htlc, a_htlc_key, b_htlc_key, revocation_key);
|
||||||
|
|
||||||
|
let our_htlc_key = derive_private_key(secp_ctx, per_commitment_point, htlc_base_key).map_err(|_| ())?;
|
||||||
|
let sighash = hash_to_message!(&bip143::SighashComponents::new(&tx).sighash_all(&tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]);
|
||||||
|
let local_tx = PublicKey::from_secret_key(&secp_ctx, &our_htlc_key) == *a_htlc_key;
|
||||||
|
let our_sig = secp_ctx.sign(&sighash, &our_htlc_key);
|
||||||
|
|
||||||
|
tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
|
||||||
|
|
||||||
|
if local_tx { // b, then a
|
||||||
|
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
|
||||||
|
tx.input[0].witness.push(our_sig.serialize_der().to_vec());
|
||||||
|
} else {
|
||||||
|
tx.input[0].witness.push(our_sig.serialize_der().to_vec());
|
||||||
|
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
|
||||||
|
}
|
||||||
|
tx.input[0].witness[1].push(SigHashType::All as u8);
|
||||||
|
tx.input[0].witness[2].push(SigHashType::All as u8);
|
||||||
|
|
||||||
|
if htlc.offered {
|
||||||
|
tx.input[0].witness.push(Vec::new());
|
||||||
|
assert!(preimage.is_none());
|
||||||
|
} else {
|
||||||
|
tx.input[0].witness.push(preimage.unwrap().0.to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.input[0].witness.push(htlc_redeemscript.as_bytes().to_vec());
|
||||||
|
|
||||||
|
Ok((our_sig, htlc_redeemscript))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
/// We use this to track local commitment transactions and put off signing them until we are ready
|
/// We use this to track local commitment transactions and put off signing them until we are ready
|
||||||
/// to broadcast. Eventually this will require a signer which is possibly external, but for now we
|
/// to broadcast. Eventually this will require a signer which is possibly external, but for now we
|
||||||
|
|
|
@ -1129,56 +1129,6 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
|
||||||
chan_utils::build_htlc_transaction(prev_hash, feerate_per_kw, if local { self.their_to_self_delay } else { self.our_to_self_delay }, htlc, &keys.a_delayed_payment_key, &keys.revocation_key)
|
chan_utils::build_htlc_transaction(prev_hash, feerate_per_kw, if local { self.their_to_self_delay } else { self.our_to_self_delay }, htlc, &keys.a_delayed_payment_key, &keys.revocation_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_htlc_tx_signature(&self, tx: &Transaction, htlc: &HTLCOutputInCommitment, keys: &TxCreationKeys) -> Result<(Script, Signature, bool), ChannelError> {
|
|
||||||
if tx.input.len() != 1 {
|
|
||||||
panic!("Tried to sign HTLC transaction that had input count != 1!");
|
|
||||||
}
|
|
||||||
|
|
||||||
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &keys);
|
|
||||||
|
|
||||||
let our_htlc_key = secp_check!(chan_utils::derive_private_key(&self.secp_ctx, &keys.per_commitment_point, self.local_keys.htlc_base_key()), "Derived invalid key, peer is maliciously selecting parameters");
|
|
||||||
let sighash = hash_to_message!(&bip143::SighashComponents::new(&tx).sighash_all(&tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]);
|
|
||||||
let is_local_tx = PublicKey::from_secret_key(&self.secp_ctx, &our_htlc_key) == keys.a_htlc_key;
|
|
||||||
Ok((htlc_redeemscript, self.secp_ctx.sign(&sighash, &our_htlc_key), is_local_tx))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
/// Signs a transaction created by build_htlc_transaction. If the transaction is an
|
|
||||||
/// HTLC-Success transaction (ie htlc.offered is false), preimage must be set!
|
|
||||||
/// TODO: Make this a chan_utils, use it in channelmonitor and tests, cause its unused now
|
|
||||||
fn sign_htlc_transaction(&self, tx: &mut Transaction, their_sig: &Signature, preimage: &Option<PaymentPreimage>, htlc: &HTLCOutputInCommitment, keys: &TxCreationKeys) -> Result<Signature, ChannelError> {
|
|
||||||
if tx.input.len() != 1 {
|
|
||||||
panic!("Tried to sign HTLC transaction that had input count != 1!");
|
|
||||||
}
|
|
||||||
if tx.input[0].witness.len() != 0 {
|
|
||||||
panic!("Tried to re-sign HTLC transaction");
|
|
||||||
}
|
|
||||||
|
|
||||||
let (htlc_redeemscript, our_sig, local_tx) = self.create_htlc_tx_signature(tx, htlc, keys)?;
|
|
||||||
|
|
||||||
tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
|
|
||||||
|
|
||||||
if local_tx { // b, then a
|
|
||||||
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
|
|
||||||
tx.input[0].witness.push(our_sig.serialize_der().to_vec());
|
|
||||||
} else {
|
|
||||||
tx.input[0].witness.push(our_sig.serialize_der().to_vec());
|
|
||||||
tx.input[0].witness.push(their_sig.serialize_der().to_vec());
|
|
||||||
}
|
|
||||||
tx.input[0].witness[1].push(SigHashType::All as u8);
|
|
||||||
tx.input[0].witness[2].push(SigHashType::All as u8);
|
|
||||||
|
|
||||||
if htlc.offered {
|
|
||||||
tx.input[0].witness.push(Vec::new());
|
|
||||||
} else {
|
|
||||||
tx.input[0].witness.push(preimage.unwrap().0.to_vec());
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.input[0].witness.push(htlc_redeemscript.into_bytes());
|
|
||||||
|
|
||||||
Ok(our_sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Per HTLC, only one get_update_fail_htlc or get_update_fulfill_htlc call may be made.
|
/// Per HTLC, only one get_update_fail_htlc or get_update_fulfill_htlc call may be made.
|
||||||
/// In such cases we debug_assert!(false) and return an IgnoreError. Thus, will always return
|
/// In such cases we debug_assert!(false) and return an IgnoreError. Thus, will always return
|
||||||
/// Ok(_) if debug assertions are turned on and preconditions are met.
|
/// Ok(_) if debug assertions are turned on and preconditions are met.
|
||||||
|
@ -1822,8 +1772,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
|
||||||
log_trace!(self, "Checking HTLC tx signature {} by key {} against tx {} with redeemscript {}", log_bytes!(msg.htlc_signatures[idx].serialize_compact()[..]), log_bytes!(local_keys.b_htlc_key.serialize()), encode::serialize_hex(&htlc_tx), encode::serialize_hex(&htlc_redeemscript));
|
log_trace!(self, "Checking HTLC tx signature {} by key {} against tx {} with redeemscript {}", log_bytes!(msg.htlc_signatures[idx].serialize_compact()[..]), log_bytes!(local_keys.b_htlc_key.serialize()), encode::serialize_hex(&htlc_tx), encode::serialize_hex(&htlc_redeemscript));
|
||||||
let htlc_sighash = hash_to_message!(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]);
|
let htlc_sighash = hash_to_message!(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]);
|
||||||
secp_check!(self.secp_ctx.verify(&htlc_sighash, &msg.htlc_signatures[idx], &local_keys.b_htlc_key), "Invalid HTLC tx signature from peer");
|
secp_check!(self.secp_ctx.verify(&htlc_sighash, &msg.htlc_signatures[idx], &local_keys.b_htlc_key), "Invalid HTLC tx signature from peer");
|
||||||
let htlc_sig = self.create_htlc_tx_signature(&htlc_tx, &htlc, &local_keys)?.1;
|
htlcs_and_sigs.push((htlc, Some(msg.htlc_signatures[idx]), source));
|
||||||
htlcs_and_sigs.push((htlc, Some((msg.htlc_signatures[idx], htlc_sig)), source));
|
|
||||||
} else {
|
} else {
|
||||||
htlcs_and_sigs.push((htlc, None, source));
|
htlcs_and_sigs.push((htlc, None, source));
|
||||||
}
|
}
|
||||||
|
@ -4265,7 +4214,7 @@ mod tests {
|
||||||
assert!(preimage.is_some());
|
assert!(preimage.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
chan.sign_htlc_transaction(&mut htlc_tx, &remote_signature, &preimage, &htlc, &keys).unwrap();
|
chan_utils::sign_htlc_transaction(&mut htlc_tx, &remote_signature, &preimage, &htlc, &keys.a_htlc_key, &keys.b_htlc_key, &keys.revocation_key, &keys.per_commitment_point, chan.local_keys.htlc_base_key(), &chan.secp_ctx).unwrap();
|
||||||
assert_eq!(serialize(&htlc_tx)[..],
|
assert_eq!(serialize(&htlc_tx)[..],
|
||||||
hex::decode($tx_hex).unwrap()[..]);
|
hex::decode($tx_hex).unwrap()[..]);
|
||||||
};
|
};
|
||||||
|
|
|
@ -337,8 +337,6 @@ enum Storage {
|
||||||
delayed_payment_base_key: SecretKey,
|
delayed_payment_base_key: SecretKey,
|
||||||
payment_base_key: SecretKey,
|
payment_base_key: SecretKey,
|
||||||
shutdown_pubkey: PublicKey,
|
shutdown_pubkey: PublicKey,
|
||||||
prev_latest_per_commitment_point: Option<PublicKey>,
|
|
||||||
latest_per_commitment_point: Option<PublicKey>,
|
|
||||||
funding_info: Option<(OutPoint, Script)>,
|
funding_info: Option<(OutPoint, Script)>,
|
||||||
current_remote_commitment_txid: Option<Sha256dHash>,
|
current_remote_commitment_txid: Option<Sha256dHash>,
|
||||||
prev_remote_commitment_txid: Option<Sha256dHash>,
|
prev_remote_commitment_txid: Option<Sha256dHash>,
|
||||||
|
@ -358,8 +356,9 @@ struct LocalSignedTx {
|
||||||
a_htlc_key: PublicKey,
|
a_htlc_key: PublicKey,
|
||||||
b_htlc_key: PublicKey,
|
b_htlc_key: PublicKey,
|
||||||
delayed_payment_key: PublicKey,
|
delayed_payment_key: PublicKey,
|
||||||
|
per_commitment_point: PublicKey,
|
||||||
feerate_per_kw: u64,
|
feerate_per_kw: u64,
|
||||||
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<(Signature, Signature)>, Option<HTLCSource>)>,
|
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
@ -741,8 +740,6 @@ impl ChannelMonitor {
|
||||||
delayed_payment_base_key: delayed_payment_base_key.clone(),
|
delayed_payment_base_key: delayed_payment_base_key.clone(),
|
||||||
payment_base_key: payment_base_key.clone(),
|
payment_base_key: payment_base_key.clone(),
|
||||||
shutdown_pubkey: shutdown_pubkey.clone(),
|
shutdown_pubkey: shutdown_pubkey.clone(),
|
||||||
prev_latest_per_commitment_point: None,
|
|
||||||
latest_per_commitment_point: None,
|
|
||||||
funding_info: None,
|
funding_info: None,
|
||||||
current_remote_commitment_txid: None,
|
current_remote_commitment_txid: None,
|
||||||
prev_remote_commitment_txid: None,
|
prev_remote_commitment_txid: None,
|
||||||
|
@ -969,9 +966,7 @@ impl ChannelMonitor {
|
||||||
/// is important that any clones of this channel monitor (including remote clones) by kept
|
/// is important that any clones of this channel monitor (including remote clones) by kept
|
||||||
/// up-to-date as our local commitment transaction is updated.
|
/// up-to-date as our local commitment transaction is updated.
|
||||||
/// Panics if set_their_to_self_delay has never been called.
|
/// Panics if set_their_to_self_delay has never been called.
|
||||||
/// Also update Storage with latest local per_commitment_point to derive local_delayedkey in
|
pub(super) fn provide_latest_local_commitment_tx_info(&mut self, commitment_tx: LocalCommitmentTransaction, local_keys: chan_utils::TxCreationKeys, feerate_per_kw: u64, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>) {
|
||||||
/// case of onchain HTLC tx
|
|
||||||
pub(super) fn provide_latest_local_commitment_tx_info(&mut self, commitment_tx: LocalCommitmentTransaction, local_keys: chan_utils::TxCreationKeys, feerate_per_kw: u64, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<(Signature, Signature)>, Option<HTLCSource>)>) {
|
|
||||||
assert!(self.their_to_self_delay.is_some());
|
assert!(self.their_to_self_delay.is_some());
|
||||||
self.prev_local_signed_commitment_tx = self.current_local_signed_commitment_tx.take();
|
self.prev_local_signed_commitment_tx = self.current_local_signed_commitment_tx.take();
|
||||||
self.current_local_signed_commitment_tx = Some(LocalSignedTx {
|
self.current_local_signed_commitment_tx = Some(LocalSignedTx {
|
||||||
|
@ -981,15 +976,10 @@ impl ChannelMonitor {
|
||||||
a_htlc_key: local_keys.a_htlc_key,
|
a_htlc_key: local_keys.a_htlc_key,
|
||||||
b_htlc_key: local_keys.b_htlc_key,
|
b_htlc_key: local_keys.b_htlc_key,
|
||||||
delayed_payment_key: local_keys.a_delayed_payment_key,
|
delayed_payment_key: local_keys.a_delayed_payment_key,
|
||||||
|
per_commitment_point: local_keys.per_commitment_point,
|
||||||
feerate_per_kw,
|
feerate_per_kw,
|
||||||
htlc_outputs,
|
htlc_outputs,
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Storage::Local { ref mut latest_per_commitment_point, .. } = self.key_storage {
|
|
||||||
*latest_per_commitment_point = Some(local_keys.per_commitment_point);
|
|
||||||
} else {
|
|
||||||
panic!("Channel somehow ended up with its internal ChannelMonitor being in Watchtower mode?");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
|
/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
|
||||||
|
@ -1152,7 +1142,7 @@ impl ChannelMonitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.key_storage {
|
match self.key_storage {
|
||||||
Storage::Local { ref funding_key, ref revocation_base_key, ref htlc_base_key, ref delayed_payment_base_key, ref payment_base_key, ref shutdown_pubkey, ref prev_latest_per_commitment_point, ref latest_per_commitment_point, ref funding_info, ref current_remote_commitment_txid, ref prev_remote_commitment_txid } => {
|
Storage::Local { ref funding_key, ref revocation_base_key, ref htlc_base_key, ref delayed_payment_base_key, ref payment_base_key, ref shutdown_pubkey, ref funding_info, ref current_remote_commitment_txid, ref prev_remote_commitment_txid } => {
|
||||||
writer.write_all(&[0; 1])?;
|
writer.write_all(&[0; 1])?;
|
||||||
writer.write_all(&funding_key[..])?;
|
writer.write_all(&funding_key[..])?;
|
||||||
writer.write_all(&revocation_base_key[..])?;
|
writer.write_all(&revocation_base_key[..])?;
|
||||||
|
@ -1160,8 +1150,6 @@ impl ChannelMonitor {
|
||||||
writer.write_all(&delayed_payment_base_key[..])?;
|
writer.write_all(&delayed_payment_base_key[..])?;
|
||||||
writer.write_all(&payment_base_key[..])?;
|
writer.write_all(&payment_base_key[..])?;
|
||||||
writer.write_all(&shutdown_pubkey.serialize())?;
|
writer.write_all(&shutdown_pubkey.serialize())?;
|
||||||
prev_latest_per_commitment_point.write(writer)?;
|
|
||||||
latest_per_commitment_point.write(writer)?;
|
|
||||||
match funding_info {
|
match funding_info {
|
||||||
&Some((ref outpoint, ref script)) => {
|
&Some((ref outpoint, ref script)) => {
|
||||||
writer.write_all(&outpoint.txid[..])?;
|
writer.write_all(&outpoint.txid[..])?;
|
||||||
|
@ -1256,15 +1244,15 @@ impl ChannelMonitor {
|
||||||
writer.write_all(&$local_tx.a_htlc_key.serialize())?;
|
writer.write_all(&$local_tx.a_htlc_key.serialize())?;
|
||||||
writer.write_all(&$local_tx.b_htlc_key.serialize())?;
|
writer.write_all(&$local_tx.b_htlc_key.serialize())?;
|
||||||
writer.write_all(&$local_tx.delayed_payment_key.serialize())?;
|
writer.write_all(&$local_tx.delayed_payment_key.serialize())?;
|
||||||
|
writer.write_all(&$local_tx.per_commitment_point.serialize())?;
|
||||||
|
|
||||||
writer.write_all(&byte_utils::be64_to_array($local_tx.feerate_per_kw))?;
|
writer.write_all(&byte_utils::be64_to_array($local_tx.feerate_per_kw))?;
|
||||||
writer.write_all(&byte_utils::be64_to_array($local_tx.htlc_outputs.len() as u64))?;
|
writer.write_all(&byte_utils::be64_to_array($local_tx.htlc_outputs.len() as u64))?;
|
||||||
for &(ref htlc_output, ref sigs, ref htlc_source) in $local_tx.htlc_outputs.iter() {
|
for &(ref htlc_output, ref sig, ref htlc_source) in $local_tx.htlc_outputs.iter() {
|
||||||
serialize_htlc_in_commitment!(htlc_output);
|
serialize_htlc_in_commitment!(htlc_output);
|
||||||
if let &Some((ref their_sig, ref our_sig)) = sigs {
|
if let &Some(ref their_sig) = sig {
|
||||||
1u8.write(writer)?;
|
1u8.write(writer)?;
|
||||||
writer.write_all(&their_sig.serialize_compact())?;
|
writer.write_all(&their_sig.serialize_compact())?;
|
||||||
writer.write_all(&our_sig.serialize_compact())?;
|
|
||||||
} else {
|
} else {
|
||||||
0u8.write(writer)?;
|
0u8.write(writer)?;
|
||||||
}
|
}
|
||||||
|
@ -2087,7 +2075,7 @@ impl ChannelMonitor {
|
||||||
} else { (None, None) }
|
} else { (None, None) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx, per_commitment_point: &Option<PublicKey>, delayed_payment_base_key: &Option<SecretKey>, height: u32) -> (Vec<Transaction>, Vec<SpendableOutputDescriptor>, Vec<TxOut>, Vec<(Sha256dHash, ClaimTxBumpMaterial)>) {
|
fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx, delayed_payment_base_key: &SecretKey, height: u32) -> (Vec<Transaction>, Vec<SpendableOutputDescriptor>, Vec<TxOut>, Vec<(Sha256dHash, ClaimTxBumpMaterial)>) {
|
||||||
let mut res = Vec::with_capacity(local_tx.htlc_outputs.len());
|
let mut res = Vec::with_capacity(local_tx.htlc_outputs.len());
|
||||||
let mut spendable_outputs = Vec::with_capacity(local_tx.htlc_outputs.len());
|
let mut spendable_outputs = Vec::with_capacity(local_tx.htlc_outputs.len());
|
||||||
let mut watch_outputs = Vec::with_capacity(local_tx.htlc_outputs.len());
|
let mut watch_outputs = Vec::with_capacity(local_tx.htlc_outputs.len());
|
||||||
|
@ -2095,18 +2083,14 @@ impl ChannelMonitor {
|
||||||
|
|
||||||
macro_rules! add_dynamic_output {
|
macro_rules! add_dynamic_output {
|
||||||
($father_tx: expr, $vout: expr) => {
|
($father_tx: expr, $vout: expr) => {
|
||||||
if let Some(ref per_commitment_point) = *per_commitment_point {
|
if let Ok(local_delayedkey) = chan_utils::derive_private_key(&self.secp_ctx, &local_tx.per_commitment_point, delayed_payment_base_key) {
|
||||||
if let Some(ref delayed_payment_base_key) = *delayed_payment_base_key {
|
spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WSH {
|
||||||
if let Ok(local_delayedkey) = chan_utils::derive_private_key(&self.secp_ctx, per_commitment_point, delayed_payment_base_key) {
|
outpoint: BitcoinOutPoint { txid: $father_tx.txid(), vout: $vout },
|
||||||
spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WSH {
|
key: local_delayedkey,
|
||||||
outpoint: BitcoinOutPoint { txid: $father_tx.txid(), vout: $vout },
|
witness_script: chan_utils::get_revokeable_redeemscript(&local_tx.revocation_key, self.our_to_self_delay, &local_tx.delayed_payment_key),
|
||||||
key: local_delayedkey,
|
to_self_delay: self.our_to_self_delay,
|
||||||
witness_script: chan_utils::get_revokeable_redeemscript(&local_tx.revocation_key, self.our_to_self_delay, &local_tx.delayed_payment_key),
|
output: $father_tx.output[$vout as usize].clone(),
|
||||||
to_self_delay: self.our_to_self_delay,
|
});
|
||||||
output: $father_tx.output[$vout as usize].clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2120,60 +2104,50 @@ impl ChannelMonitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for &(ref htlc, ref sigs, _) in local_tx.htlc_outputs.iter() {
|
if let &Storage::Local { ref htlc_base_key, .. } = &self.key_storage {
|
||||||
if let Some(transaction_output_index) = htlc.transaction_output_index {
|
for &(ref htlc, ref sigs, _) in local_tx.htlc_outputs.iter() {
|
||||||
if let &Some((ref their_sig, ref our_sig)) = sigs {
|
if let Some(transaction_output_index) = htlc.transaction_output_index {
|
||||||
if htlc.offered {
|
if let &Some(ref their_sig) = sigs {
|
||||||
log_trace!(self, "Broadcasting HTLC-Timeout transaction against local commitment transactions");
|
if htlc.offered {
|
||||||
let mut htlc_timeout_tx = chan_utils::build_htlc_transaction(&local_tx.txid, local_tx.feerate_per_kw, self.their_to_self_delay.unwrap(), htlc, &local_tx.delayed_payment_key, &local_tx.revocation_key);
|
log_trace!(self, "Broadcasting HTLC-Timeout transaction against local commitment transactions");
|
||||||
|
let mut htlc_timeout_tx = chan_utils::build_htlc_transaction(&local_tx.txid, local_tx.feerate_per_kw, self.their_to_self_delay.unwrap(), htlc, &local_tx.delayed_payment_key, &local_tx.revocation_key);
|
||||||
|
let (our_sig, htlc_script) = match
|
||||||
|
chan_utils::sign_htlc_transaction(&mut htlc_timeout_tx, their_sig, &None, htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key, &local_tx.per_commitment_point, htlc_base_key, &self.secp_ctx) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(_) => continue,
|
||||||
|
};
|
||||||
|
|
||||||
htlc_timeout_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
|
add_dynamic_output!(htlc_timeout_tx, 0);
|
||||||
|
|
||||||
htlc_timeout_tx.input[0].witness.push(their_sig.serialize_der().to_vec());
|
|
||||||
htlc_timeout_tx.input[0].witness[1].push(SigHashType::All as u8);
|
|
||||||
htlc_timeout_tx.input[0].witness.push(our_sig.serialize_der().to_vec());
|
|
||||||
htlc_timeout_tx.input[0].witness[2].push(SigHashType::All as u8);
|
|
||||||
|
|
||||||
htlc_timeout_tx.input[0].witness.push(Vec::new());
|
|
||||||
let htlc_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key);
|
|
||||||
htlc_timeout_tx.input[0].witness.push(htlc_script.clone().into_bytes());
|
|
||||||
|
|
||||||
add_dynamic_output!(htlc_timeout_tx, 0);
|
|
||||||
let height_timer = Self::get_height_timer(height, htlc.cltv_expiry);
|
|
||||||
let mut per_input_material = HashMap::with_capacity(1);
|
|
||||||
per_input_material.insert(htlc_timeout_tx.input[0].previous_output, InputMaterial::LocalHTLC { script: htlc_script, sigs: (*their_sig, *our_sig), preimage: None, amount: htlc.amount_msat / 1000});
|
|
||||||
//TODO: with option_simplified_commitment track outpoint too
|
|
||||||
log_trace!(self, "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}", htlc_timeout_tx.input[0].previous_output.vout, htlc_timeout_tx.input[0].previous_output.txid, height_timer);
|
|
||||||
pending_claims.push((htlc_timeout_tx.txid(), ClaimTxBumpMaterial { height_timer, feerate_previous: 0, soonest_timelock: htlc.cltv_expiry, per_input_material }));
|
|
||||||
res.push(htlc_timeout_tx);
|
|
||||||
} else {
|
|
||||||
if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
|
|
||||||
log_trace!(self, "Broadcasting HTLC-Success transaction against local commitment transactions");
|
|
||||||
let mut htlc_success_tx = chan_utils::build_htlc_transaction(&local_tx.txid, local_tx.feerate_per_kw, self.their_to_self_delay.unwrap(), htlc, &local_tx.delayed_payment_key, &local_tx.revocation_key);
|
|
||||||
|
|
||||||
htlc_success_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
|
|
||||||
|
|
||||||
htlc_success_tx.input[0].witness.push(their_sig.serialize_der().to_vec());
|
|
||||||
htlc_success_tx.input[0].witness[1].push(SigHashType::All as u8);
|
|
||||||
htlc_success_tx.input[0].witness.push(our_sig.serialize_der().to_vec());
|
|
||||||
htlc_success_tx.input[0].witness[2].push(SigHashType::All as u8);
|
|
||||||
|
|
||||||
htlc_success_tx.input[0].witness.push(payment_preimage.0.to_vec());
|
|
||||||
let htlc_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key);
|
|
||||||
htlc_success_tx.input[0].witness.push(htlc_script.clone().into_bytes());
|
|
||||||
|
|
||||||
add_dynamic_output!(htlc_success_tx, 0);
|
|
||||||
let height_timer = Self::get_height_timer(height, htlc.cltv_expiry);
|
let height_timer = Self::get_height_timer(height, htlc.cltv_expiry);
|
||||||
let mut per_input_material = HashMap::with_capacity(1);
|
let mut per_input_material = HashMap::with_capacity(1);
|
||||||
per_input_material.insert(htlc_success_tx.input[0].previous_output, InputMaterial::LocalHTLC { script: htlc_script, sigs: (*their_sig, *our_sig), preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000});
|
per_input_material.insert(htlc_timeout_tx.input[0].previous_output, InputMaterial::LocalHTLC { script: htlc_script, sigs: (*their_sig, our_sig), preimage: None, amount: htlc.amount_msat / 1000});
|
||||||
//TODO: with option_simplified_commitment track outpoint too
|
//TODO: with option_simplified_commitment track outpoint too
|
||||||
log_trace!(self, "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}", htlc_success_tx.input[0].previous_output.vout, htlc_success_tx.input[0].previous_output.txid, height_timer);
|
log_trace!(self, "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}", htlc_timeout_tx.input[0].previous_output.vout, htlc_timeout_tx.input[0].previous_output.txid, height_timer);
|
||||||
pending_claims.push((htlc_success_tx.txid(), ClaimTxBumpMaterial { height_timer, feerate_previous: 0, soonest_timelock: htlc.cltv_expiry, per_input_material }));
|
pending_claims.push((htlc_timeout_tx.txid(), ClaimTxBumpMaterial { height_timer, feerate_previous: 0, soonest_timelock: htlc.cltv_expiry, per_input_material }));
|
||||||
res.push(htlc_success_tx);
|
res.push(htlc_timeout_tx);
|
||||||
|
} else {
|
||||||
|
if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
|
||||||
|
log_trace!(self, "Broadcasting HTLC-Success transaction against local commitment transactions");
|
||||||
|
let mut htlc_success_tx = chan_utils::build_htlc_transaction(&local_tx.txid, local_tx.feerate_per_kw, self.their_to_self_delay.unwrap(), htlc, &local_tx.delayed_payment_key, &local_tx.revocation_key);
|
||||||
|
let (our_sig, htlc_script) = match
|
||||||
|
chan_utils::sign_htlc_transaction(&mut htlc_success_tx, their_sig, &Some(*payment_preimage), htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key, &local_tx.per_commitment_point, htlc_base_key, &self.secp_ctx) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(_) => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
add_dynamic_output!(htlc_success_tx, 0);
|
||||||
|
let height_timer = Self::get_height_timer(height, htlc.cltv_expiry);
|
||||||
|
let mut per_input_material = HashMap::with_capacity(1);
|
||||||
|
per_input_material.insert(htlc_success_tx.input[0].previous_output, InputMaterial::LocalHTLC { script: htlc_script, sigs: (*their_sig, our_sig), preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000});
|
||||||
|
//TODO: with option_simplified_commitment track outpoint too
|
||||||
|
log_trace!(self, "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}", htlc_success_tx.input[0].previous_output.vout, htlc_success_tx.input[0].previous_output.txid, height_timer);
|
||||||
|
pending_claims.push((htlc_success_tx.txid(), ClaimTxBumpMaterial { height_timer, feerate_previous: 0, soonest_timelock: htlc.cltv_expiry, per_input_material }));
|
||||||
|
res.push(htlc_success_tx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
watch_outputs.push(local_tx.tx.without_valid_witness().output[transaction_output_index as usize].clone());
|
||||||
watch_outputs.push(local_tx.tx.without_valid_witness().output[transaction_output_index as usize].clone());
|
} else { panic!("Should have sigs for non-dust local tx outputs!") }
|
||||||
} else { panic!("Should have sigs for non-dust local tx outputs!") }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2245,12 +2219,10 @@ impl ChannelMonitor {
|
||||||
log_trace!(self, "Got latest local commitment tx broadcast, searching for available HTLCs to claim");
|
log_trace!(self, "Got latest local commitment tx broadcast, searching for available HTLCs to claim");
|
||||||
assert!(local_tx.tx.has_local_sig());
|
assert!(local_tx.tx.has_local_sig());
|
||||||
match self.key_storage {
|
match self.key_storage {
|
||||||
Storage::Local { ref delayed_payment_base_key, ref latest_per_commitment_point, .. } => {
|
Storage::Local { ref delayed_payment_base_key, .. } => {
|
||||||
append_onchain_update!(self.broadcast_by_local_state(local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key), height));
|
append_onchain_update!(self.broadcast_by_local_state(local_tx, delayed_payment_base_key, height));
|
||||||
},
|
},
|
||||||
Storage::Watchtower { .. } => {
|
Storage::Watchtower { .. } => { }
|
||||||
append_onchain_update!(self.broadcast_by_local_state(local_tx, &None, &None, height));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2270,12 +2242,10 @@ impl ChannelMonitor {
|
||||||
log_trace!(self, "Got previous local commitment tx broadcast, searching for available HTLCs to claim");
|
log_trace!(self, "Got previous local commitment tx broadcast, searching for available HTLCs to claim");
|
||||||
assert!(local_tx.tx.has_local_sig());
|
assert!(local_tx.tx.has_local_sig());
|
||||||
match self.key_storage {
|
match self.key_storage {
|
||||||
Storage::Local { ref delayed_payment_base_key, ref prev_latest_per_commitment_point, .. } => {
|
Storage::Local { ref delayed_payment_base_key, .. } => {
|
||||||
append_onchain_update!(self.broadcast_by_local_state(local_tx, prev_latest_per_commitment_point, &Some(*delayed_payment_base_key), height));
|
append_onchain_update!(self.broadcast_by_local_state(local_tx, delayed_payment_base_key, height));
|
||||||
},
|
},
|
||||||
Storage::Watchtower { .. } => {
|
Storage::Watchtower { .. } => { }
|
||||||
append_onchain_update!(self.broadcast_by_local_state(local_tx, &None, &None, height));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2351,8 +2321,8 @@ impl ChannelMonitor {
|
||||||
if let &Some(ref local_tx) = &self.current_local_signed_commitment_tx {
|
if let &Some(ref local_tx) = &self.current_local_signed_commitment_tx {
|
||||||
let mut res = vec![local_tx.tx.with_valid_witness().clone()];
|
let mut res = vec![local_tx.tx.with_valid_witness().clone()];
|
||||||
match self.key_storage {
|
match self.key_storage {
|
||||||
Storage::Local { ref delayed_payment_base_key, ref prev_latest_per_commitment_point, .. } => {
|
Storage::Local { ref delayed_payment_base_key, .. } => {
|
||||||
res.append(&mut self.broadcast_by_local_state(local_tx, prev_latest_per_commitment_point, &Some(*delayed_payment_base_key), 0).0);
|
res.append(&mut self.broadcast_by_local_state(local_tx, delayed_payment_base_key, 0).0);
|
||||||
// We throw away the generated waiting_first_conf data as we aren't (yet) confirmed and we don't actually know what the caller wants to do.
|
// We throw away the generated waiting_first_conf data as we aren't (yet) confirmed and we don't actually know what the caller wants to do.
|
||||||
// The data will be re-generated and tracked in check_spend_local_transaction if we get a confirmation.
|
// The data will be re-generated and tracked in check_spend_local_transaction if we get a confirmation.
|
||||||
},
|
},
|
||||||
|
@ -2524,8 +2494,8 @@ impl ChannelMonitor {
|
||||||
log_trace!(self, "Broadcast onchain {}", log_tx!(cur_local_tx.tx.with_valid_witness()));
|
log_trace!(self, "Broadcast onchain {}", log_tx!(cur_local_tx.tx.with_valid_witness()));
|
||||||
broadcaster.broadcast_transaction(&cur_local_tx.tx.with_valid_witness());
|
broadcaster.broadcast_transaction(&cur_local_tx.tx.with_valid_witness());
|
||||||
match self.key_storage {
|
match self.key_storage {
|
||||||
Storage::Local { ref delayed_payment_base_key, ref latest_per_commitment_point, .. } => {
|
Storage::Local { ref delayed_payment_base_key, .. } => {
|
||||||
let (txs, mut spendable_output, new_outputs, _) = self.broadcast_by_local_state(&cur_local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key), height);
|
let (txs, mut spendable_output, new_outputs, _) = self.broadcast_by_local_state(&cur_local_tx, delayed_payment_base_key, height);
|
||||||
spendable_outputs.append(&mut spendable_output);
|
spendable_outputs.append(&mut spendable_output);
|
||||||
if !new_outputs.is_empty() {
|
if !new_outputs.is_empty() {
|
||||||
watch_outputs.push((cur_local_tx.txid.clone(), new_outputs));
|
watch_outputs.push((cur_local_tx.txid.clone(), new_outputs));
|
||||||
|
@ -2535,17 +2505,7 @@ impl ChannelMonitor {
|
||||||
broadcaster.broadcast_transaction(&tx);
|
broadcaster.broadcast_transaction(&tx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Storage::Watchtower { .. } => {
|
Storage::Watchtower { .. } => { },
|
||||||
let (txs, mut spendable_output, new_outputs, _) = self.broadcast_by_local_state(&cur_local_tx, &None, &None, height);
|
|
||||||
spendable_outputs.append(&mut spendable_output);
|
|
||||||
if !new_outputs.is_empty() {
|
|
||||||
watch_outputs.push((cur_local_tx.txid.clone(), new_outputs));
|
|
||||||
}
|
|
||||||
for tx in txs {
|
|
||||||
log_trace!(self, "Broadcast onchain {}", log_tx!(tx));
|
|
||||||
broadcaster.broadcast_transaction(&tx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3010,8 +2970,6 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
|
||||||
let delayed_payment_base_key = Readable::read(reader)?;
|
let delayed_payment_base_key = Readable::read(reader)?;
|
||||||
let payment_base_key = Readable::read(reader)?;
|
let payment_base_key = Readable::read(reader)?;
|
||||||
let shutdown_pubkey = Readable::read(reader)?;
|
let shutdown_pubkey = Readable::read(reader)?;
|
||||||
let prev_latest_per_commitment_point = Readable::read(reader)?;
|
|
||||||
let latest_per_commitment_point = Readable::read(reader)?;
|
|
||||||
// Technically this can fail and serialize fail a round-trip, but only for serialization of
|
// Technically this can fail and serialize fail a round-trip, but only for serialization of
|
||||||
// barely-init'd ChannelMonitors that we can't do anything with.
|
// barely-init'd ChannelMonitors that we can't do anything with.
|
||||||
let outpoint = OutPoint {
|
let outpoint = OutPoint {
|
||||||
|
@ -3028,8 +2986,6 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
|
||||||
delayed_payment_base_key,
|
delayed_payment_base_key,
|
||||||
payment_base_key,
|
payment_base_key,
|
||||||
shutdown_pubkey,
|
shutdown_pubkey,
|
||||||
prev_latest_per_commitment_point,
|
|
||||||
latest_per_commitment_point,
|
|
||||||
funding_info,
|
funding_info,
|
||||||
current_remote_commitment_txid,
|
current_remote_commitment_txid,
|
||||||
prev_remote_commitment_txid,
|
prev_remote_commitment_txid,
|
||||||
|
@ -3130,6 +3086,7 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
|
||||||
let a_htlc_key = Readable::read(reader)?;
|
let a_htlc_key = Readable::read(reader)?;
|
||||||
let b_htlc_key = Readable::read(reader)?;
|
let b_htlc_key = Readable::read(reader)?;
|
||||||
let delayed_payment_key = Readable::read(reader)?;
|
let delayed_payment_key = Readable::read(reader)?;
|
||||||
|
let per_commitment_point = Readable::read(reader)?;
|
||||||
let feerate_per_kw: u64 = Readable::read(reader)?;
|
let feerate_per_kw: u64 = Readable::read(reader)?;
|
||||||
|
|
||||||
let htlcs_len: u64 = Readable::read(reader)?;
|
let htlcs_len: u64 = Readable::read(reader)?;
|
||||||
|
@ -3138,7 +3095,7 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
|
||||||
let htlc = read_htlc_in_commitment!();
|
let htlc = read_htlc_in_commitment!();
|
||||||
let sigs = match <u8 as Readable<R>>::read(reader)? {
|
let sigs = match <u8 as Readable<R>>::read(reader)? {
|
||||||
0 => None,
|
0 => None,
|
||||||
1 => Some((Readable::read(reader)?, Readable::read(reader)?)),
|
1 => Some(Readable::read(reader)?),
|
||||||
_ => return Err(DecodeError::InvalidValue),
|
_ => return Err(DecodeError::InvalidValue),
|
||||||
};
|
};
|
||||||
htlcs.push((htlc, sigs, Readable::read(reader)?));
|
htlcs.push((htlc, sigs, Readable::read(reader)?));
|
||||||
|
@ -3146,7 +3103,7 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
|
||||||
|
|
||||||
LocalSignedTx {
|
LocalSignedTx {
|
||||||
txid: tx.txid(),
|
txid: tx.txid(),
|
||||||
tx, revocation_key, a_htlc_key, b_htlc_key, delayed_payment_key, feerate_per_kw,
|
tx, revocation_key, a_htlc_key, b_htlc_key, delayed_payment_key, per_commitment_point, feerate_per_kw,
|
||||||
htlc_outputs: htlcs
|
htlc_outputs: htlcs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue