// TODO: We should refactor this to be an Inbound/OutboundChannel until initial setup handshaking
// has been completed, and then turn into a Channel to get compiler-time enforcement of things like
- // calling get_channel_id() before we're set up or things like get_outbound_funding_signed on an
+ // calling channel_id() before we're set up or things like get_outbound_funding_signed on an
// inbound channel.
pub struct Channel {
user_id: u64,
local_keys: ChannelKeys,
+ // Our commitment numbers start at 2^48-1 and count down, whereas the ones used in transaction
+ // generation start at 0 and count up...this simplifies some parts of implementation at the
+ // cost of others, but should really just be changed.
+
cur_local_commitment_transaction_number: u64,
cur_remote_commitment_transaction_number: u64,
value_to_self_msat: u64, // Excluding all pending_htlcs, excluding fees
/// Guaranteed to return a value no larger than channel_value_satoshis
fn get_our_channel_reserve_satoshis(channel_value_satoshis: u64) -> u64 {
- cmp::min(channel_value_satoshis, 10) //TODO
+ cmp::min(channel_value_satoshis, 1000) //TODO
}
fn derive_our_dust_limit_satoshis(at_open_background_feerate: u64) -> u64 {
if msg.dust_limit_satoshis > msg.funding_satoshis {
return Err(HandleError{err: "Peer never wants payout outputs?", msg: Some(msgs::ErrorAction::DisconnectPeer{})});
}
- if msg.max_htlc_value_in_flight_msat > msg.funding_satoshis * 1000 {
- return Err(HandleError{err: "Bogus max_htlc_value_in_flight_satoshis", msg: Some(msgs::ErrorAction::DisconnectPeer{})});
- }
if msg.htlc_minimum_msat >= (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000 {
return Err(HandleError{err: "Minimum htlc value is full channel value", msg: Some(msgs::ErrorAction::DisconnectPeer{})});
}
channel_value_satoshis: msg.funding_satoshis,
their_dust_limit_satoshis: msg.dust_limit_satoshis,
our_dust_limit_satoshis: Channel::derive_our_dust_limit_satoshis(background_feerate),
- their_max_htlc_value_in_flight_msat: msg.max_htlc_value_in_flight_msat,
+ their_max_htlc_value_in_flight_msat: cmp::min(msg.max_htlc_value_in_flight_msat, msg.funding_satoshis * 1000),
their_channel_reserve_satoshis: msg.channel_reserve_satoshis,
their_htlc_minimum_msat: msg.htlc_minimum_msat,
our_htlc_minimum_msat: Channel::derive_our_htlc_minimum_msat(msg.feerate_per_kw as u64),
/// 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) -> (Transaction, Vec<HTLCOutputInCommitment>) {
- let obscured_commitment_transaction_number = self.get_commitment_transaction_number_obscure_factor() ^ commitment_number;
+ let obscured_commitment_transaction_number = self.get_commitment_transaction_number_obscure_factor() ^ (0xffffffffffff - commitment_number);
let txins = {
let mut ins: Vec<TxIn> = Vec::new();
if msg.dust_limit_satoshis > 21000000 * 100000000 {
return Err(HandleError{err: "Peer never wants payout outputs?", msg: None});
}
- if msg.max_htlc_value_in_flight_msat > self.channel_value_satoshis * 1000 {
- return Err(HandleError{err: "Bogus max_htlc_value_in_flight_satoshis", msg: None});
- }
if msg.channel_reserve_satoshis > self.channel_value_satoshis {
return Err(HandleError{err: "Bogus channel_reserve_satoshis", msg: None});
}
self.channel_monitor.set_their_htlc_base_key(&msg.htlc_basepoint);
self.their_dust_limit_satoshis = msg.dust_limit_satoshis;
- self.their_max_htlc_value_in_flight_msat = msg.max_htlc_value_in_flight_msat;
+ self.their_max_htlc_value_in_flight_msat = cmp::min(msg.max_htlc_value_in_flight_msat, self.channel_value_satoshis * 1000);
self.their_channel_reserve_satoshis = msg.channel_reserve_satoshis;
self.their_htlc_minimum_msat = msg.htlc_minimum_msat;
self.their_to_self_delay = msg.to_self_delay;
}
let funding_txo = OutPoint::new(msg.funding_txid, msg.funding_output_index);
- self.channel_monitor.set_funding_info(funding_txo);
+ let funding_txo_script = self.get_funding_redeemscript().to_v0_p2wsh();
+ self.channel_monitor.set_funding_info((funding_txo, funding_txo_script));
let (remote_initial_commitment_tx, our_signature) = match self.funding_created_signature(&msg.signature) {
Ok(res) => res,
panic!("Should not have advanced channel commitment tx numbers prior to funding_created");
}
- self.channel_monitor.set_funding_info(funding_txo);
+ let funding_txo_script = self.get_funding_redeemscript().to_v0_p2wsh();
+ self.channel_monitor.set_funding_info((funding_txo, funding_txo_script));
let (our_signature, commitment_tx) = match self.get_outbound_funding_created_signature() {
Ok(res) => res,
use bitcoin::util::hash::Sha256dHash;
use bitcoin::util::bip143;
use bitcoin::network::serialize::serialize;
+ use bitcoin::blockdata::script::Script;
use bitcoin::blockdata::transaction::Transaction;
use ln::channel::{Channel,ChannelKeys,HTLCOutput,HTLCState,HTLCOutputInCommitment,TxCreationKeys};
use ln::channel::MAX_FUNDING_SATOSHIS;
chan.our_dust_limit_satoshis = 546;
let funding_info = OutPoint::new(Sha256dHash::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), 0);
- chan.channel_monitor.set_funding_info(funding_info);
+ chan.channel_monitor.set_funding_info((funding_info, Script::new()));
chan.their_payment_basepoint = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &hex_bytes("4444444444444444444444444444444444444444444444444444444444444444").unwrap()[..]).unwrap()).unwrap();
assert_eq!(chan.their_payment_basepoint.serialize()[..],
macro_rules! test_commitment {
( $their_sig_hex: expr, $our_sig_hex: expr, $tx_hex: expr) => {
- unsigned_tx = chan.build_commitment_transaction(42, &keys, true, false);
+ unsigned_tx = chan.build_commitment_transaction(0xffffffffffff - 42, &keys, true, false);
let their_signature = Signature::from_der(&secp_ctx, &hex_bytes($their_sig_hex).unwrap()[..]).unwrap();
let sighash = Message::from_slice(&bip143::SighashComponents::new(&unsigned_tx.0).sighash_all(&unsigned_tx.0.input[0], &chan.get_funding_redeemscript(), chan.channel_value_satoshis)[..]).unwrap();
secp_ctx.verify(&sighash, &their_signature, &chan.their_funding_pubkey).unwrap();
match forward_chan.send_htlc(forward_info.amt_to_forward, forward_info.payment_hash, forward_info.outgoing_cltv_value, forward_info.onion_packet.unwrap()) {
Err(_e) => {
let chan_update = self.get_channel_update(forward_chan).unwrap();
- failed_forwards.push((forward_info.payment_hash, 0x4000 | 7, Some(chan_update)));
+ failed_forwards.push((forward_info.payment_hash, 0x1000 | 7, Some(chan_update)));
continue;
},
Ok(update_add) => {
}
}
+ /// We force-close the channel without letting our counterparty participate in the shutdown
fn block_disconnected(&self, header: &BlockHeader) {
- let mut channel_state = self.channel_state.lock().unwrap();
- for channel in channel_state.by_id.values_mut() {
- if channel.block_disconnected(header) {
- //TODO Close channel here
+ let mut channel_lock = self.channel_state.lock().unwrap();
+ let channel_state = channel_lock.borrow_parts();
+ let short_to_id = channel_state.short_to_id;
+ channel_state.by_id.retain(|_, v| {
+ if v.block_disconnected(header) {
+ let tx = v.force_shutdown();
+ for broadcast_tx in tx {
+ self.tx_broadcaster.broadcast_transaction(&broadcast_tx);
+ }
+ if let Some(short_id) = v.get_short_channel_id() {
+ short_to_id.remove(&short_id);
+ }
+ false
+ } else {
+ true
}
- }
+ });
}
}
let chan = channel_state.by_id.get_mut(&forwarding_id).unwrap();
if !chan.is_live() {
let chan_update = self.get_channel_update(chan).unwrap();
- return_err!("Forwarding channel is not in a ready state.", 0x4000 | 7, &chan_update.encode_with_len()[..]);
+ return_err!("Forwarding channel is not in a ready state.", 0x1000 | 7, &chan_update.encode_with_len()[..]);
}
}
mod tests {
use chain::chaininterface;
use chain::transaction::OutPoint;
+ use chain::chaininterface::ChainListener;
use ln::channelmanager::{ChannelManager,OnionKeys};
use ln::router::{Route, RouteHop, Router};
use ln::msgs;
assert_eq!(node.chan_monitor.added_monitors.lock().unwrap().len(), 0);
}
}
+
+ #[test]
+ fn test_unconf_chan() {
+ // After creating a chan between nodes, we disconnect all blocks previously seen to force a channel close on nodes[0] side
+ let nodes = create_network(2);
+ create_announced_chan_between_nodes(&nodes, 0, 1);
+
+ let channel_state = nodes[0].node.channel_state.lock().unwrap();
+ assert_eq!(channel_state.by_id.len(), 1);
+ assert_eq!(channel_state.short_to_id.len(), 1);
+ mem::drop(channel_state);
+
+ let mut headers = Vec::new();
+ let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+ headers.push(header.clone());
+ for _i in 2..100 {
+ header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+ headers.push(header.clone());
+ }
+ while !headers.is_empty() {
+ nodes[0].node.block_disconnected(&headers.pop().unwrap());
+ }
+ let channel_state = nodes[0].node.channel_state.lock().unwrap();
+ assert_eq!(channel_state.by_id.len(), 0);
+ assert_eq!(channel_state.short_to_id.len(), 0);
+ }
}