/// generated by the peer which proposed adding the HTLCs, and thus we need to understand both
/// which peer generated this transaction and "to whom" this transaction flows.
#[inline]
- fn build_commitment_transaction(&self, commitment_number: u64, keys: &TxCreationKeys, local: bool, generated_by_local: bool, feerate_per_kw: u64) -> (Transaction, Vec<HTLCOutputInCommitment>) {
+ fn build_commitment_transaction(&self, commitment_number: u64, keys: &TxCreationKeys, local: bool, generated_by_local: bool, feerate_per_kw: u64) -> (Transaction, Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>) {
let obscured_commitment_transaction_number = self.get_commitment_transaction_number_obscure_factor() ^ (INITIAL_COMMITMENT_NUMBER - commitment_number);
let txins = {
ins
};
- let mut txouts: Vec<(TxOut, Option<HTLCOutputInCommitment>)> = Vec::with_capacity(self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len() + 2);
+ let mut txouts: Vec<(TxOut, Option<(HTLCOutputInCommitment, Option<HTLCSource>)>)> = Vec::with_capacity(self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len() + 2);
let dust_limit_satoshis = if local { self.our_dust_limit_satoshis } else { self.their_dust_limit_satoshis };
let mut remote_htlc_total_msat = 0;
let mut value_to_self_msat_offset = 0;
macro_rules! add_htlc_output {
- ($htlc: expr, $outbound: expr) => {
+ ($htlc: expr, $outbound: expr, $source: expr) => {
if $outbound == local { // "offered HTLC output"
if $htlc.amount_msat / 1000 >= dust_limit_satoshis + (feerate_per_kw * HTLC_TIMEOUT_TX_WEIGHT / 1000) {
let htlc_in_tx = get_htlc_in_commitment!($htlc, true);
txouts.push((TxOut {
script_pubkey: chan_utils::get_htlc_redeemscript(&htlc_in_tx, &keys).to_v0_p2wsh(),
value: $htlc.amount_msat / 1000
- }, Some(htlc_in_tx)));
+ }, Some((htlc_in_tx, $source))));
}
} else {
if $htlc.amount_msat / 1000 >= dust_limit_satoshis + (feerate_per_kw * HTLC_SUCCESS_TX_WEIGHT / 1000) {
txouts.push((TxOut { // "received HTLC output"
script_pubkey: chan_utils::get_htlc_redeemscript(&htlc_in_tx, &keys).to_v0_p2wsh(),
value: $htlc.amount_msat / 1000
- }, Some(htlc_in_tx)));
+ }, Some((htlc_in_tx, $source))));
}
}
}
};
if include {
- add_htlc_output!(htlc, false);
+ add_htlc_output!(htlc, false, None);
remote_htlc_total_msat += htlc.amount_msat;
} else {
match &htlc.state {
};
if include {
- add_htlc_output!(htlc, true);
+ add_htlc_output!(htlc, true, Some(htlc.source.clone()));
local_htlc_total_msat += htlc.amount_msat;
} else {
match htlc.state {
transaction_utils::sort_outputs(&mut txouts);
let mut outputs: Vec<TxOut> = Vec::with_capacity(txouts.len());
- let mut htlcs_used: Vec<HTLCOutputInCommitment> = Vec::with_capacity(txouts.len());
+ let mut htlcs_used: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)> = Vec::with_capacity(txouts.len());
for (idx, out) in txouts.drain(..).enumerate() {
outputs.push(out.0);
- if let Some(out_htlc) = out.1 {
- htlcs_used.push(out_htlc);
- htlcs_used.last_mut().unwrap().transaction_output_index = idx as u32;
+ if let Some(mut out_htlc) = out.1 {
+ out_htlc.0.transaction_output_index = idx as u32;
+ htlcs_used.push((out_htlc.0, out_htlc.1));
}
}
new_local_commitment_txn.push(local_commitment_tx.0.clone());
let mut htlcs_and_sigs = Vec::with_capacity(local_commitment_tx.1.len());
- for (idx, ref htlc) in local_commitment_tx.1.iter().enumerate() {
+ for (idx, &(ref htlc, ref htlc_source)) in local_commitment_tx.1.iter().enumerate() {
let mut htlc_tx = self.build_htlc_transaction(&local_commitment_txid, htlc, true, &local_keys, feerate_per_kw);
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &local_keys);
let htlc_sighash = Message::from_slice(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]).unwrap();
} else {
self.create_htlc_tx_signature(&htlc_tx, htlc, &local_keys)?.1
};
- htlcs_and_sigs.push(((*htlc).clone(), msg.htlc_signatures[idx], htlc_sig));
+ htlcs_and_sigs.push(((*htlc).clone(), msg.htlc_signatures[idx], htlc_sig, (*htlc_source).clone()));
}
let next_per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &self.build_local_commitment_secret(self.cur_local_commitment_transaction_number - 1));
/// Only fails in case of bad keys. Used for channel_reestablish commitment_signed generation
/// when we shouldn't change HTLC/channel state.
- fn send_commitment_no_state_update(&self) -> Result<(msgs::CommitmentSigned, (Transaction, Vec<HTLCOutputInCommitment>)), ChannelError> {
+ fn send_commitment_no_state_update(&self) -> Result<(msgs::CommitmentSigned, (Transaction, Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>)), ChannelError> {
let funding_script = self.get_funding_redeemscript();
let mut feerate_per_kw = self.feerate_per_kw;
let mut htlc_sigs = Vec::new();
- for ref htlc in remote_commitment_tx.1.iter() {
+ for &(ref htlc, _) in remote_commitment_tx.1.iter() {
let htlc_tx = self.build_htlc_transaction(&remote_commitment_txid, htlc, false, &remote_keys, feerate_per_kw);
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &remote_keys);
let htlc_sighash = Message::from_slice(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]).unwrap();
let htlc_basepoint = PublicKey::from_secret_key(&secp_ctx, &chan.local_keys.htlc_base_key);
let keys = TxCreationKeys::new(&secp_ctx, &per_commitment_point, &delayed_payment_base, &htlc_basepoint, &chan.their_revocation_basepoint.unwrap(), &chan.their_payment_basepoint.unwrap(), &chan.their_htlc_basepoint.unwrap()).unwrap();
- let mut unsigned_tx: (Transaction, Vec<HTLCOutputInCommitment>);
+ let mut unsigned_tx: (Transaction, Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>);
macro_rules! test_commitment {
( $their_sig_hex: expr, $our_sig_hex: expr, $tx_hex: expr) => {
( $htlc_idx: expr, $their_sig_hex: expr, $our_sig_hex: expr, $tx_hex: expr ) => {
let remote_signature = Signature::from_der(&secp_ctx, &hex::decode($their_sig_hex).unwrap()[..]).unwrap();
- let ref htlc = unsigned_tx.1[$htlc_idx];
+ let (ref htlc, _) = unsigned_tx.1[$htlc_idx];
let mut htlc_tx = chan.build_htlc_transaction(&unsigned_tx.0.txid(), &htlc, true, &keys, chan.feerate_per_kw);
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &keys);
let htlc_sighash = Message::from_slice(&bip143::SighashComponents::new(&htlc_tx).sighash_all(&htlc_tx.input[0], &htlc_redeemscript, htlc.amount_msat / 1000)[..]).unwrap();
use ln::msgs::DecodeError;
use ln::chan_utils;
use ln::chan_utils::HTLCOutputInCommitment;
+use ln::channelmanager::{HTLCSource, HTLCPreviousHopData};
+use ln::router::{Route, RouteHop};
use chain::chaininterface::{ChainListener, ChainWatchInterface, BroadcasterInterface};
use chain::transaction::OutPoint;
use chain::keysinterface::SpendableOutputDescriptor;
b_htlc_key: PublicKey,
delayed_payment_key: PublicKey,
feerate_per_kw: u64,
- htlc_outputs: Vec<(HTLCOutputInCommitment, Signature, Signature)>,
+ htlc_outputs: Vec<(HTLCOutputInCommitment, Signature, Signature, Option<HTLCSource>)>,
}
const SERIALIZATION_VERSION: u8 = 1;
their_to_self_delay: Option<u16>,
old_secrets: [([u8; 32], u64); 49],
- remote_claimable_outpoints: HashMap<Sha256dHash, Vec<HTLCOutputInCommitment>>,
+ remote_claimable_outpoints: HashMap<Sha256dHash, Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>>,
/// We cannot identify HTLC-Success or HTLC-Timeout transactions by themselves on the chain.
/// Nor can we figure out their commitment numbers without the commitment transaction they are
/// spending. Thus, in order to claim them via revocation key, we track all the remote
let remote_hash_commitment_number = &mut self.remote_hash_commitment_number;
self.payment_preimages.retain(|&k, _| {
- for &(ref htlc, _, _) in &local_signed_commitment_tx.htlc_outputs {
+ for &(ref htlc, _, _, _) in &local_signed_commitment_tx.htlc_outputs {
if k == htlc.payment_hash {
return true
}
}
if let Some(prev_local_commitment_tx) = prev_local_signed_commitment_tx {
- for &(ref htlc, _, _) in prev_local_commitment_tx.htlc_outputs.iter() {
+ for &(ref htlc, _, _, _) in prev_local_commitment_tx.htlc_outputs.iter() {
if k == htlc.payment_hash {
return true
}
/// The monitor watches for it to be broadcasted and then uses the HTLC information (and
/// possibly future revocation/preimage information) to claim outputs where possible.
/// We cache also the mapping hash:commitment number to lighten pruning of old preimages by watchtowers.
- pub(super) fn provide_latest_remote_commitment_tx_info(&mut self, unsigned_commitment_tx: &Transaction, htlc_outputs: Vec<HTLCOutputInCommitment>, commitment_number: u64, their_revocation_point: PublicKey) {
+ pub(super) fn provide_latest_remote_commitment_tx_info(&mut self, unsigned_commitment_tx: &Transaction, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<HTLCSource>)>, commitment_number: u64, their_revocation_point: PublicKey) {
// TODO: Encrypt the htlc_outputs data with the single-hash of the commitment transaction
// so that a remote monitor doesn't learn anything unless there is a malicious close.
// (only maybe, sadly we cant do the same for local info, as we need to be aware of
// timeouts)
- for htlc in &htlc_outputs {
+ for &(ref htlc, _) in &htlc_outputs {
self.remote_hash_commitment_number.insert(htlc.payment_hash, commitment_number);
}
+ // We prune old claimable outpoints, useless to pass backward state when remote commitment
+ // tx get revoked, optimize for storage
+ for (_, htlc_data) in self.remote_claimable_outpoints.iter_mut() {
+ for &mut(_, ref mut source) in htlc_data.iter_mut() {
+ source.take();
+ }
+ }
self.remote_claimable_outpoints.insert(unsigned_commitment_tx.txid(), htlc_outputs);
self.current_remote_commitment_number = commitment_number;
//TODO: Merge this into the other per-remote-transaction output storage stuff
/// 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
/// case of onchain HTLC tx
- pub(super) fn provide_latest_local_commitment_tx_info(&mut self, signed_commitment_tx: Transaction, local_keys: chan_utils::TxCreationKeys, feerate_per_kw: u64, htlc_outputs: Vec<(HTLCOutputInCommitment, Signature, Signature)>) {
+ pub(super) fn provide_latest_local_commitment_tx_info(&mut self, signed_commitment_tx: Transaction, local_keys: chan_utils::TxCreationKeys, feerate_per_kw: u64, htlc_outputs: Vec<(HTLCOutputInCommitment, Signature, Signature, Option<HTLCSource>)>) {
assert!(self.their_to_self_delay.is_some());
self.prev_local_signed_commitment_tx = self.current_local_signed_commitment_tx.take();
self.current_local_signed_commitment_tx = Some(LocalSignedTx {
}
}
+ macro_rules! serialize_htlc_source {
+ ($htlc_source: expr) => {
+ if let &Some(ref source) = $htlc_source {
+ writer.write_all(&[1; 1])?;
+ match source {
+ &HTLCSource::PreviousHopData(ref source) => { serialize_htlc_previous_hop_data!(source); },
+ &HTLCSource::OutboundRoute { ref route, ref session_priv, ref first_hop_htlc_msat } => { serialize_htlc_outbound_route!(route, session_priv, *first_hop_htlc_msat); },
+ }
+ } else {
+ writer.write_all(&[0; 1])?;
+ }
+ }
+ }
+
+ macro_rules! serialize_htlc_previous_hop_data {
+ ($htlc_source: expr) => {
+ writer.write_all(&[0; 1])?;
+ writer.write_all(&byte_utils::be64_to_array($htlc_source.short_channel_id))?;
+ writer.write_all(&byte_utils::be64_to_array($htlc_source.htlc_id))?;
+ writer.write_all(&$htlc_source.incoming_packet_shared_secret[..])?;
+ }
+ }
+
+ macro_rules! serialize_htlc_outbound_route {
+ ($route: expr, $session_priv: expr, $first_hop_htlc_msat: expr) => {
+ writer.write_all(&[1; 1])?;
+ serialize_route!($route);
+ writer.write_all(&$session_priv[..])?;
+ writer.write_all(&byte_utils::be64_to_array($first_hop_htlc_msat))?;
+ }
+ }
+
+ macro_rules! serialize_route {
+ ($route: expr) => {
+ writer.write_all(&byte_utils::be64_to_array($route.hops.len() as u64))?;
+ for hop in &$route.hops {
+ writer.write_all(&hop.pubkey.serialize())?;
+ writer.write_all(&byte_utils::be64_to_array(hop.short_channel_id))?;
+ writer.write_all(&byte_utils::be64_to_array(hop.fee_msat))?;
+ writer.write_all(&byte_utils::be32_to_array(hop.cltv_expiry_delta))?;
+ }
+ }
+ }
+
writer.write_all(&byte_utils::be64_to_array(self.remote_claimable_outpoints.len() as u64))?;
- for (ref txid, ref htlc_outputs) in self.remote_claimable_outpoints.iter() {
+ for (ref txid, ref htlc_infos) in self.remote_claimable_outpoints.iter() {
writer.write_all(&txid[..])?;
- writer.write_all(&byte_utils::be64_to_array(htlc_outputs.len() as u64))?;
- for htlc_output in htlc_outputs.iter() {
+ writer.write_all(&byte_utils::be64_to_array(htlc_infos.len() as u64))?;
+ for &(ref htlc_output, ref htlc_source) in htlc_infos.iter() {
serialize_htlc_in_commitment!(htlc_output);
+ serialize_htlc_source!(htlc_source);
}
}
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))?;
- for &(ref htlc_output, ref their_sig, ref our_sig) in $local_tx.htlc_outputs.iter() {
+ for &(ref htlc_output, ref their_sig, ref our_sig, ref htlc_source) in $local_tx.htlc_outputs.iter() {
serialize_htlc_in_commitment!(htlc_output);
writer.write_all(&their_sig.serialize_compact(&self.secp_ctx))?;
writer.write_all(&our_sig.serialize_compact(&self.secp_ctx))?;
+ serialize_htlc_source!(htlc_source);
}
}
}
let (sig, redeemscript) = match self.key_storage {
Storage::Local { ref revocation_base_key, .. } => {
let redeemscript = if $htlc_idx.is_none() { revokeable_redeemscript.clone() } else {
- let htlc = &per_commitment_option.unwrap()[$htlc_idx.unwrap()];
+ let htlc = &per_commitment_option.unwrap()[$htlc_idx.unwrap()].0;
chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey)
};
let sighash = ignore_error!(Message::from_slice(&$sighash_parts.sighash_all(&$input, &redeemscript, $amount)[..]));
if let Some(per_commitment_data) = per_commitment_option {
inputs.reserve_exact(per_commitment_data.len());
- for (idx, htlc) in per_commitment_data.iter().enumerate() {
+ for (idx, &(ref htlc, _)) in per_commitment_data.iter().enumerate() {
let expected_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey);
if htlc.transaction_output_index as usize >= tx.output.len() ||
tx.output[htlc.transaction_output_index as usize].value != htlc.amount_msat / 1000 ||
{
let (sig, redeemscript) = match self.key_storage {
Storage::Local { ref htlc_base_key, .. } => {
- let htlc = &per_commitment_option.unwrap()[$input.sequence as usize];
+ let htlc = &per_commitment_option.unwrap()[$input.sequence as usize].0;
let redeemscript = chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey);
let sighash = ignore_error!(Message::from_slice(&$sighash_parts.sighash_all(&$input, &redeemscript, $amount)[..]));
let htlc_key = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &htlc_base_key));
}
}
- for (idx, htlc) in per_commitment_data.iter().enumerate() {
+ for (idx, &(ref htlc, _)) in per_commitment_data.iter().enumerate() {
if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
let input = TxIn {
previous_output: BitcoinOutPoint {
}
}
- for &(ref htlc, ref their_sig, ref our_sig) in local_tx.htlc_outputs.iter() {
+ for &(ref htlc, ref their_sig, ref our_sig, _) in local_tx.htlc_outputs.iter() {
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);
pub(super) fn would_broadcast_at_height(&self, height: u32) -> bool {
if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx {
- for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() {
+ for &(ref htlc, _, _, _) in cur_local_tx.htlc_outputs.iter() {
// For inbound HTLCs which we know the preimage for, we have to ensure we hit the
// chain with enough room to claim the HTLC without our counterparty being able to
// time out the HTLC first.
}
}
+ macro_rules! read_htlc_source {
+ () => {
+ {
+ match <u8 as Readable<R>>::read(reader)? {
+ 0 => None,
+ 1 => {
+ match <u8 as Readable<R>>::read(reader)? {
+ 0 => Some(HTLCSource::PreviousHopData(read_htlc_previous_hop_data!())),
+ 1 => Some(read_htlc_outbound_route!()),
+ _ => return Err(DecodeError::InvalidValue),
+ }
+ },
+ _ => return Err(DecodeError::InvalidValue),
+ }
+ }
+ }
+ }
+
+ macro_rules! read_htlc_previous_hop_data {
+ () => {
+ {
+ let short_channel_id: u64 = Readable::read(reader)?;
+ let htlc_id: u64 = Readable::read(reader)?;
+ let incoming_packet_shared_secret: [u8; 32] = Readable::read(reader)?;
+
+ HTLCPreviousHopData {
+ short_channel_id, htlc_id, incoming_packet_shared_secret
+ }
+ }
+ }
+ }
+
+ macro_rules! read_htlc_outbound_route {
+ () => {
+ {
+ let route = read_route!();
+ let session_priv = Readable::read(reader)?;
+ let first_hop_htlc_msat = Readable::read(reader)?;
+
+ HTLCSource::OutboundRoute {
+ route, session_priv, first_hop_htlc_msat
+ }
+ }
+ }
+ }
+
+ macro_rules! read_route {
+ () => {
+ {
+ let route_len: u64 = Readable::read(reader)?;
+ let mut hops = Vec::with_capacity(cmp::min(route_len as usize, MAX_ALLOC_SIZE / 64));
+ for _ in 0..route_len {
+ let pubkey = Readable::read(reader)?;
+ let short_channel_id = Readable::read(reader)?;
+ let fee_msat = Readable::read(reader)?;
+ let cltv_expiry_delta = Readable::read(reader)?;
+
+ hops.push(RouteHop { pubkey, short_channel_id, fee_msat, cltv_expiry_delta });
+ }
+ Route {
+ hops
+ }
+ }
+ }
+ }
+
let remote_claimable_outpoints_len: u64 = Readable::read(reader)?;
let mut remote_claimable_outpoints = HashMap::with_capacity(cmp::min(remote_claimable_outpoints_len as usize, MAX_ALLOC_SIZE / 64));
for _ in 0..remote_claimable_outpoints_len {
let outputs_count: u64 = Readable::read(reader)?;
let mut outputs = Vec::with_capacity(cmp::min(outputs_count as usize, MAX_ALLOC_SIZE / 32));
for _ in 0..outputs_count {
- outputs.push(read_htlc_in_commitment!());
+ let out = read_htlc_in_commitment!();
+ let source = read_htlc_source!();
+ outputs.push((out, source));
}
if let Some(_) = remote_claimable_outpoints.insert(txid, outputs) {
return Err(DecodeError::InvalidValue);
let htlc_outputs_len: u64 = Readable::read(reader)?;
let mut htlc_outputs = Vec::with_capacity(cmp::min(htlc_outputs_len as usize, MAX_ALLOC_SIZE / 128));
for _ in 0..htlc_outputs_len {
- htlc_outputs.push((read_htlc_in_commitment!(), Readable::read(reader)?, Readable::read(reader)?));
+ let out = read_htlc_in_commitment!();
+ let sigs = (Readable::read(reader)?, Readable::read(reader)?);
+ let source = read_htlc_source!();
+ htlc_outputs.push((out, sigs.0, sigs.1, source));
}
LocalSignedTx {
use hex;
use ln::channelmonitor::ChannelMonitor;
use ln::chan_utils::{HTLCOutputInCommitment, TxCreationKeys};
+ use ln::channelmanager::{HTLCSource, HTLCPreviousHopData};
use util::sha2::Sha256;
use util::test_utils::TestLogger;
use secp256k1::key::{SecretKey,PublicKey};
}
}
let dummy_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
+ let dummy_source = HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id: 0, htlc_id: 0, incoming_packet_shared_secret: [0; 32]});
let mut preimages = Vec::new();
{
{
let mut res = Vec::new();
for (idx, preimage) in $preimages_slice.iter().enumerate() {
- res.push(HTLCOutputInCommitment {
+ res.push((HTLCOutputInCommitment {
offered: true,
amount_msat: 0,
cltv_expiry: 0,
payment_hash: preimage.1.clone(),
transaction_output_index: idx as u32,
- });
+ }, Some(dummy_source.clone())));
}
res
}
($preimages_slice: expr) => {
{
let mut inp = preimages_slice_to_htlc_outputs!($preimages_slice);
- let res: Vec<_> = inp.drain(..).map(|e| { (e, dummy_sig.clone(), dummy_sig.clone()) }).collect();
+ let res: Vec<_> = inp.drain(..).map(|e| { (e.0, dummy_sig.clone(), dummy_sig.clone(), e.1) }).collect();
res
}
}