use ln::features::{ChannelFeatures, InitFeatures};
use ln::msgs;
use ln::msgs::{DecodeError, OptionalField, DataLossProtect};
-use ln::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, HTLC_FAIL_BACK_BUFFER};
use ln::channelmanager::{PendingHTLCStatus, HTLCSource, HTLCFailReason, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, PaymentPreimage, PaymentHash, BREAKDOWN_TIMEOUT, MAX_LOCAL_BREAKDOWN_TIMEOUT};
-use ln::chan_utils::{CounterpartyCommitmentSecrets, LocalCommitmentTransaction, TxCreationKeys, HTLCOutputInCommitment, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT, make_funding_redeemscript, ChannelPublicKeys, PreCalculatedTxCreationKeys};
+use ln::chan_utils::{CounterpartyCommitmentSecrets, HolderCommitmentTransaction, TxCreationKeys, HTLCOutputInCommitment, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT, make_funding_redeemscript, ChannelPublicKeys, PreCalculatedTxCreationKeys};
use ln::chan_utils;
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
-use chain::transaction::OutPoint;
+use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, HTLC_FAIL_BACK_BUFFER};
+use chain::transaction::{OutPoint, TransactionData};
use chain::keysinterface::{ChannelKeys, KeysInterface};
use util::transaction_utils;
use util::ser::{Readable, Writeable, Writer};
// has been completed, and then turn into a Channel to get compiler-time enforcement of things like
// calling channel_id() before we're set up or things like get_outbound_funding_signed on an
// inbound channel.
+//
+// Holder designates channel data owned for the benefice of the user client.
+// Counterparty designates channel data owned by the another channel participant entity.
pub(super) struct Channel<ChanSigner: ChannelKeys> {
config: ChannelConfig,
let feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
Ok(Channel {
- user_id: user_id,
+ user_id,
config: config.channel_options.clone(),
channel_id: keys_provider.get_secure_random_bytes(),
channel_state: ChannelState::OurInitSent as u32,
channel_outbound: true,
secp_ctx: Secp256k1::new(),
- channel_value_satoshis: channel_value_satoshis,
+ channel_value_satoshis,
latest_monitor_update_id: 0,
} else { None };
let chan = Channel {
- user_id: user_id,
+ user_id,
config: local_config,
channel_id: msg.temporary_channel_id,
feerate_per_kw: msg.feerate_per_kw,
channel_value_satoshis: msg.funding_satoshis,
counterparty_dust_limit_satoshis: msg.dust_limit_satoshis,
- holder_dust_limit_satoshis: holder_dust_limit_satoshis,
+ holder_dust_limit_satoshis,
counterparty_max_htlc_value_in_flight_msat: cmp::min(msg.max_htlc_value_in_flight_msat, msg.funding_satoshis * 1000),
counterparty_selected_channel_reserve_satoshis: msg.channel_reserve_satoshis,
counterparty_htlc_minimum_msat: msg.htlc_minimum_msat,
}
#[inline]
- fn get_closing_transaction_weight(a_scriptpubkey: &Script, b_scriptpubkey: &Script) -> u64 {
- (4 + 1 + 36 + 4 + 1 + 1 + 2*(8+1) + 4 + a_scriptpubkey.len() as u64 + b_scriptpubkey.len() as u64)*4 + 2 + 1 + 1 + 2*(1 + 72)
+ fn get_closing_transaction_weight(&self, a_scriptpubkey: Option<&Script>, b_scriptpubkey: Option<&Script>) -> u64 {
+ let mut ret =
+ (4 + // version
+ 1 + // input count
+ 36 + // prevout
+ 1 + // script length (0)
+ 4 + // sequence
+ 1 + // output count
+ 4 // lock time
+ )*4 + // * 4 for non-witness parts
+ 2 + // witness marker and flag
+ 1 + // witness element count
+ 4 + // 4 element lengths (2 sigs, multisig dummy, and witness script)
+ self.get_funding_redeemscript().len() as u64 + // funding witness script
+ 2*(1 + 71); // two signatures + sighash type flags
+ if let Some(spk) = a_scriptpubkey {
+ ret += ((8+1) + // output values and script length
+ spk.len() as u64) * 4; // scriptpubkey and witness multiplier
+ }
+ if let Some(spk) = b_scriptpubkey {
+ ret += ((8+1) + // output values and script length
+ spk.len() as u64) * 4; // scriptpubkey and witness multiplier
+ }
+ ret
}
#[inline]
Ok(())
}
- fn funding_created_signature<L: Deref>(&mut self, sig: &Signature, logger: &L) -> Result<(Transaction, LocalCommitmentTransaction, Signature), ChannelError> where L::Target: Logger {
+ fn funding_created_signature<L: Deref>(&mut self, sig: &Signature, logger: &L) -> Result<(Transaction, HolderCommitmentTransaction, Signature), ChannelError> where L::Target: Logger {
let funding_script = self.get_funding_redeemscript();
let keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number)?;
log_trace!(logger, "Checking funding_created tx signature {} by key {} against tx {} (sighash {}) with redeemscript {}", log_bytes!(sig.serialize_compact()[..]), log_bytes!(self.counterparty_funding_pubkey().serialize()), encode::serialize_hex(&initial_commitment_tx), log_bytes!(sighash[..]), encode::serialize_hex(&funding_script));
secp_check!(self.secp_ctx.verify(&sighash, &sig, self.counterparty_funding_pubkey()), "Invalid funding_created signature from peer".to_owned());
- let tx = LocalCommitmentTransaction::new_missing_local_sig(initial_commitment_tx, sig.clone(), &self.holder_keys.pubkeys().funding_pubkey, self.counterparty_funding_pubkey(), keys, self.feerate_per_kw, Vec::new());
+ let tx = HolderCommitmentTransaction::new_missing_holder_sig(initial_commitment_tx, sig.clone(), &self.holder_keys.pubkeys().funding_pubkey, self.counterparty_funding_pubkey(), keys, self.feerate_per_kw, Vec::new());
let counterparty_keys = self.build_remote_transaction_keys()?;
let counterparty_initial_commitment_tx = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, self.feerate_per_kw, logger).0;
Ok((msgs::FundingSigned {
channel_id: self.channel_id,
- signature: signature
+ signature
}, channel_monitor))
}
let funding_txo_script = funding_redeemscript.to_v0_p2wsh();
macro_rules! create_monitor {
() => { {
- let commitment_tx = LocalCommitmentTransaction::new_missing_local_sig(initial_commitment_tx.clone(), msg.signature.clone(), &self.holder_keys.pubkeys().funding_pubkey, counterparty_funding_pubkey, holder_keys.clone(), self.feerate_per_kw, Vec::new());
+ let commitment_tx = HolderCommitmentTransaction::new_missing_holder_sig(initial_commitment_tx.clone(), msg.signature.clone(), &self.holder_keys.pubkeys().funding_pubkey, counterparty_funding_pubkey, holder_keys.clone(), self.feerate_per_kw, Vec::new());
let mut channel_monitor = ChannelMonitor::new(self.holder_keys.clone(),
&self.shutdown_pubkey, self.holder_selected_contest_delay,
&self.destination_script, (funding_txo.clone(), funding_txo_script.clone()),
let mut monitor_update = ChannelMonitorUpdate {
update_id: self.latest_monitor_update_id,
updates: vec![ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo {
- commitment_tx: LocalCommitmentTransaction::new_missing_local_sig(commitment_tx.0, msg.signature.clone(), &self.holder_keys.pubkeys().funding_pubkey, &counterparty_funding_pubkey, keys, self.feerate_per_kw, htlcs_without_source),
+ commitment_tx: HolderCommitmentTransaction::new_missing_holder_sig(commitment_tx.0, msg.signature.clone(), &self.holder_keys.pubkeys().funding_pubkey, &counterparty_funding_pubkey, keys, self.feerate_per_kw, htlcs_without_source),
htlc_outputs: htlcs_and_sigs
}]
};
Ok((msgs::RevokeAndACK {
channel_id: self.channel_id,
- per_commitment_secret: per_commitment_secret,
- next_per_commitment_point: next_per_commitment_point,
+ per_commitment_secret,
+ next_per_commitment_point,
}, commitment_signed, closing_signed, monitor_update))
}
update_fulfill_htlcs,
update_fail_htlcs,
update_fail_malformed_htlcs: Vec::new(),
- update_fee: update_fee,
+ update_fee,
commitment_signed,
}, monitor_update)), htlcs_to_fail))
} else {
Some(msgs::UpdateFee {
channel_id: self.channel_id,
- feerate_per_kw: feerate_per_kw,
+ feerate_per_kw,
})
}
let next_per_commitment_point = self.holder_keys.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx);
Some(msgs::FundingLocked {
channel_id: self.channel_id(),
- next_per_commitment_point: next_per_commitment_point,
+ next_per_commitment_point,
})
} else { None };
let next_per_commitment_point = self.holder_keys.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx);
return Ok((Some(msgs::FundingLocked {
channel_id: self.channel_id(),
- next_per_commitment_point: next_per_commitment_point,
+ next_per_commitment_point,
}), None, None, None, RAACommitmentOrder::CommitmentFirst, shutdown_msg));
}
let next_per_commitment_point = self.holder_keys.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx);
Some(msgs::FundingLocked {
channel_id: self.channel_id(),
- next_per_commitment_point: next_per_commitment_point,
+ next_per_commitment_point,
})
} else { None };
if self.feerate_per_kw > proposed_feerate {
proposed_feerate = self.feerate_per_kw;
}
- let tx_weight = Self::get_closing_transaction_weight(&self.get_closing_scriptpubkey(), self.counterparty_shutdown_scriptpubkey.as_ref().unwrap());
+ let tx_weight = self.get_closing_transaction_weight(Some(&self.get_closing_scriptpubkey()), Some(self.counterparty_shutdown_scriptpubkey.as_ref().unwrap()));
let proposed_total_fee_satoshis = proposed_feerate as u64 * tx_weight / 1000;
let (closing_tx, total_fee_satoshis) = self.build_closing_transaction(proposed_total_fee_satoshis, false);
let sig = self.holder_keys
.sign_closing_transaction(&closing_tx, &self.secp_ctx)
.ok();
+ assert!(closing_tx.get_weight() as u64 <= tx_weight);
if sig.is_none() { return None; }
self.last_sent_closing_fee = Some((proposed_feerate, total_fee_satoshis, sig.clone().unwrap()));
if !self.pending_inbound_htlcs.is_empty() || !self.pending_outbound_htlcs.is_empty() {
return Err(ChannelError::Close("Remote end sent us a closing_signed while there were still pending HTLCs".to_owned()));
}
- if msg.fee_satoshis > 21000000 * 10000000 { //this is required to stop potential overflow in build_closing_transaction
+ if msg.fee_satoshis > 21_000_000 * 1_0000_0000 { //this is required to stop potential overflow in build_closing_transaction
return Err(ChannelError::Close("Remote tried to send us a closing tx with > 21 million BTC fee".to_owned()));
}
},
};
+ let closing_tx_max_weight = self.get_closing_transaction_weight(
+ if let Some(oup) = closing_tx.output.get(0) { Some(&oup.script_pubkey) } else { None },
+ if let Some(oup) = closing_tx.output.get(1) { Some(&oup.script_pubkey) } else { None });
if let Some((_, last_fee, sig)) = self.last_sent_closing_fee {
if last_fee == msg.fee_satoshis {
self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
+ assert!(closing_tx.get_weight() as u64 <= closing_tx_max_weight);
+ debug_assert!(closing_tx.get_weight() as u64 >= closing_tx_max_weight - 2);
self.channel_state = ChannelState::ShutdownComplete as u32;
self.update_time_counter += 1;
return Ok((None, Some(closing_tx)));
macro_rules! propose_new_feerate {
($new_feerate: expr) => {
- let closing_tx_max_weight = Self::get_closing_transaction_weight(&self.get_closing_scriptpubkey(), self.counterparty_shutdown_scriptpubkey.as_ref().unwrap());
- let (closing_tx, used_total_fee) = self.build_closing_transaction($new_feerate as u64 * closing_tx_max_weight / 1000, false);
+ let tx_weight = self.get_closing_transaction_weight(Some(&self.get_closing_scriptpubkey()), Some(self.counterparty_shutdown_scriptpubkey.as_ref().unwrap()));
+ let (closing_tx, used_total_fee) = self.build_closing_transaction($new_feerate as u64 * tx_weight / 1000, false);
let sig = self.holder_keys
.sign_closing_transaction(&closing_tx, &self.secp_ctx)
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
+ assert!(closing_tx.get_weight() as u64 <= tx_weight);
self.last_sent_closing_fee = Some(($new_feerate, used_total_fee, sig.clone()));
return Ok((Some(msgs::ClosingSigned {
channel_id: self.channel_id,
}
}
- let proposed_sat_per_kw = msg.fee_satoshis * 1000 / closing_tx.get_weight() as u64;
+ let mut min_feerate = 253;
if self.channel_outbound {
let max_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
- if (proposed_sat_per_kw as u32) > max_feerate {
+ if (msg.fee_satoshis as u64) > max_feerate as u64 * closing_tx_max_weight / 1000 {
if let Some((last_feerate, _, _)) = self.last_sent_closing_fee {
if max_feerate <= last_feerate {
return Err(ChannelError::Close(format!("Unable to come to consensus about closing feerate, remote wanted something higher ({}) than our Normal feerate ({})", last_feerate, max_feerate)));
propose_new_feerate!(max_feerate);
}
} else {
- let min_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
- if (proposed_sat_per_kw as u32) < min_feerate {
- if let Some((last_feerate, _, _)) = self.last_sent_closing_fee {
- if min_feerate >= last_feerate {
- return Err(ChannelError::Close(format!("Unable to come to consensus about closing feerate, remote wanted something lower ({}) than our Background feerate ({}).", last_feerate, min_feerate)));
- }
+ min_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
+ }
+ if (msg.fee_satoshis as u64) < min_feerate as u64 * closing_tx_max_weight / 1000 {
+ if let Some((last_feerate, _, _)) = self.last_sent_closing_fee {
+ if min_feerate >= last_feerate {
+ return Err(ChannelError::Close(format!("Unable to come to consensus about closing feerate, remote wanted something lower ({}) than our Background feerate ({}).", last_feerate, min_feerate)));
}
- propose_new_feerate!(min_feerate);
}
+ propose_new_feerate!(min_feerate);
}
let sig = self.holder_keys
.sign_closing_transaction(&closing_tx, &self.secp_ctx)
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
+ assert!(closing_tx.get_weight() as u64 <= closing_tx_max_weight);
+ debug_assert!(closing_tx.get_weight() as u64 >= closing_tx_max_weight - 2);
self.channel_state = ChannelState::ShutdownComplete as u32;
self.update_time_counter += 1;
}
/// Allowed in any state (including after shutdown)
+ #[cfg(test)]
pub fn get_holder_htlc_minimum_msat(&self) -> u64 {
self.holder_htlc_minimum_msat
}
/// Allowed in any state (including after shutdown)
pub fn get_counterparty_htlc_minimum_msat(&self) -> u64 {
- self.holder_htlc_minimum_msat
+ self.counterparty_htlc_minimum_msat
}
pub fn get_value_satoshis(&self) -> u64 {
///
/// May return some HTLCs (and their payment_hash) which have timed out and should be failed
/// back.
- pub fn block_connected(&mut self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[usize]) -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> {
+ pub fn block_connected(&mut self, header: &BlockHeader, txdata: &TransactionData, height: u32) -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> {
let mut timed_out_htlcs = Vec::new();
self.holding_cell_htlc_updates.retain(|htlc_update| {
match htlc_update {
}
}
if non_shutdown_state & !(ChannelState::TheirFundingLocked as u32) == ChannelState::FundingSent as u32 {
- for (ref tx, index_in_block) in txn_matched.iter().zip(indexes_of_txn_matched) {
+ for &(index_in_block, tx) in txdata.iter() {
if tx.txid() == self.funding_txo.unwrap().txid {
let txo_idx = self.funding_txo.unwrap().index as usize;
if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.get_funding_redeemscript().to_v0_p2wsh() ||
}
}
}
- if height > 0xff_ff_ff || (*index_in_block) > 0xff_ff_ff {
+ if height > 0xff_ff_ff || (index_in_block) > 0xff_ff_ff {
panic!("Block was bogus - either height 16 million or had > 16 million transactions");
}
assert!(txo_idx <= 0xffff); // txo_idx is a (u16 as usize), so this is just listed here for completeness
self.funding_tx_confirmations = 1;
- self.short_channel_id = Some(((height as u64) << (5*8)) |
- ((*index_in_block as u64) << (2*8)) |
- ((txo_idx as u64) << (0*8)));
+ self.short_channel_id = Some(((height as u64) << (5*8)) |
+ ((index_in_block as u64) << (2*8)) |
+ ((txo_idx as u64) << (0*8)));
}
}
}
let next_per_commitment_point = self.holder_keys.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx);
return Ok((Some(msgs::FundingLocked {
channel_id: self.channel_id,
- next_per_commitment_point: next_per_commitment_point,
+ next_per_commitment_point,
}), timed_out_htlcs));
} else {
self.monitor_pending_funding_locked = true;
let keys = self.holder_keys.pubkeys();
msgs::OpenChannel {
- chain_hash: chain_hash,
+ chain_hash,
temporary_channel_id: self.channel_id,
funding_satoshis: self.channel_value_satoshis,
push_msat: self.channel_value_satoshis * 1000 - self.value_to_self_msat,
temporary_channel_id,
funding_txid: funding_txo.txid,
funding_output_index: funding_txo.index,
- signature: signature
+ signature
})
}
let msg = msgs::UnsignedChannelAnnouncement {
features: ChannelFeatures::known(),
- chain_hash: chain_hash,
+ chain_hash,
short_channel_id: self.get_short_channel_id().unwrap(),
node_id_1: if were_node_one { node_id } else { self.get_counterparty_node_id() },
node_id_2: if were_node_one { self.get_counterparty_node_id() } else { node_id },
}
}
- for _htlc in self.pending_outbound_htlcs.drain(..) {
- //TODO: Do something with the remaining HTLCs
- //(we need to have the ChannelManager monitor them so we can claim the inbound HTLCs
- //which correspond)
- }
-
self.channel_state = ChannelState::ShutdownComplete as u32;
self.update_time_counter += 1;
self.latest_monitor_update_id += 1;
use ln::features::InitFeatures;
use ln::msgs::{OptionalField, DataLossProtect};
use ln::chan_utils;
- use ln::chan_utils::{LocalCommitmentTransaction, ChannelPublicKeys};
+ use ln::chan_utils::{HolderCommitmentTransaction, ChannelPublicKeys};
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
use chain::keysinterface::{InMemoryChannelKeys, KeysInterface};
use chain::transaction::OutPoint;
let mut unsigned_tx: (Transaction, Vec<HTLCOutputInCommitment>);
- let mut localtx;
+ let mut holdertx;
macro_rules! test_commitment {
( $counterparty_sig_hex: expr, $sig_hex: expr, $tx_hex: expr, {
$( { $htlc_idx: expr, $counterparty_htlc_sig_hex: expr, $htlc_sig_hex: expr, $htlc_tx_hex: expr } ), *
})*
assert_eq!(unsigned_tx.1.len(), per_htlc.len());
- localtx = LocalCommitmentTransaction::new_missing_local_sig(unsigned_tx.0.clone(), counterparty_signature.clone(), &chan_keys.pubkeys().funding_pubkey, chan.counterparty_funding_pubkey(), keys.clone(), chan.feerate_per_kw, per_htlc);
- let local_sig = chan_keys.sign_holder_commitment(&localtx, &chan.secp_ctx).unwrap();
- assert_eq!(Signature::from_der(&hex::decode($sig_hex).unwrap()[..]).unwrap(), local_sig);
+ holdertx = HolderCommitmentTransaction::new_missing_holder_sig(unsigned_tx.0.clone(), counterparty_signature.clone(), &chan_keys.pubkeys().funding_pubkey, chan.counterparty_funding_pubkey(), keys.clone(), chan.feerate_per_kw, per_htlc);
+ let holder_sig = chan_keys.sign_holder_commitment(&holdertx, &chan.secp_ctx).unwrap();
+ assert_eq!(Signature::from_der(&hex::decode($sig_hex).unwrap()[..]).unwrap(), holder_sig);
- assert_eq!(serialize(&localtx.add_local_sig(&redeemscript, local_sig))[..],
+ assert_eq!(serialize(&holdertx.add_holder_sig(&redeemscript, holder_sig))[..],
hex::decode($tx_hex).unwrap()[..]);
- let htlc_sigs = chan_keys.sign_holder_commitment_htlc_transactions(&localtx, &chan.secp_ctx).unwrap();
- let mut htlc_sig_iter = localtx.per_htlc.iter().zip(htlc_sigs.iter().enumerate());
+ let htlc_sigs = chan_keys.sign_holder_commitment_htlc_transactions(&holdertx, &chan.secp_ctx).unwrap();
+ let mut htlc_sig_iter = holdertx.per_htlc.iter().zip(htlc_sigs.iter().enumerate());
$({
let remote_signature = Signature::from_der(&hex::decode($counterparty_htlc_sig_hex).unwrap()[..]).unwrap();
let signature = Signature::from_der(&hex::decode($htlc_sig_hex).unwrap()[..]).unwrap();
assert_eq!(Some(signature), *(htlc_sig.1).1);
- assert_eq!(serialize(&localtx.get_signed_htlc_tx((htlc_sig.1).0, &(htlc_sig.1).1.unwrap(), &preimage, chan.counterparty_selected_contest_delay))[..],
+ assert_eq!(serialize(&holdertx.get_signed_htlc_tx((htlc_sig.1).0, &(htlc_sig.1).1.unwrap(), &preimage, chan.counterparty_selected_contest_delay))[..],
hex::decode($htlc_tx_hex).unwrap()[..]);
})*
loop {