pub commitment_seed: [u8; 32],
}
+#[cfg(test)]
+pub struct ChannelValueStat {
+ pub value_to_self_msat: u64,
+ pub channel_value_msat: u64,
+ pub channel_reserve_msat: u64,
+ pub pending_outbound_htlcs_amount_msat: u64,
+ pub pending_inbound_htlcs_amount_msat: u64,
+ pub holding_cell_outbound_amount_msat: u64,
+ pub their_max_htlc_value_in_flight_msat: u64, // outgoing
+}
+
impl ChannelKeys {
pub fn new_from_seed(seed: &[u8; 32]) -> Result<ChannelKeys, secp256k1::Error> {
let mut prk = [0; 32];
}
}
-#[derive(PartialEq)]
+enum InboundHTLCRemovalReason {
+ FailRelay(msgs::OnionErrorPacket),
+ FailMalformed(([u8; 32], u16)),
+ Fulfill([u8; 32]),
+}
+
enum InboundHTLCState {
/// Added by remote, to be included in next local commitment tx.
- RemoteAnnounced,
+ RemoteAnnounced(PendingHTLCStatus),
/// Included in a received commitment_signed message (implying we've revoke_and_ack'ed it), but
/// the remote side hasn't yet revoked their previous state, which we need them to do before we
/// accept this HTLC. Implies AwaitingRemoteRevoke.
/// We also have not yet included this HTLC in a commitment_signed message, and are waiting on
/// a remote revoke_and_ack on a previous state before we can do so.
- AwaitingRemoteRevokeToAnnounce,
+ AwaitingRemoteRevokeToAnnounce(PendingHTLCStatus),
/// Included in a received commitment_signed message (implying we've revoke_and_ack'ed it), but
/// the remote side hasn't yet revoked their previous state, which we need them to do before we
/// accept this HTLC. Implies AwaitingRemoteRevoke.
/// We have included this HTLC in our latest commitment_signed and are now just waiting on a
/// revoke_and_ack.
- AwaitingAnnouncedRemoteRevoke,
+ AwaitingAnnouncedRemoteRevoke(PendingHTLCStatus),
Committed,
/// Removed by us and a new commitment_signed was sent (if we were AwaitingRemoteRevoke when we
/// created it we would have put it in the holding cell instead). When they next revoke_and_ack
/// ChannelMonitor::would_broadcast_at_height) so we actually remove the HTLC from our own
/// local state before then, once we're sure that the next commitment_signed and
/// ChannelMonitor::provide_latest_local_commitment_tx_info will not include this HTLC.
- LocalRemoved,
+ LocalRemoved(InboundHTLCRemovalReason),
}
struct InboundHTLCOutput {
cltv_expiry: u32,
payment_hash: [u8; 32],
state: InboundHTLCState,
- /// If we're in LocalRemoved, set to true if we fulfilled the HTLC, and can claim money
- local_removed_fulfilled: bool,
- /// state pre-Committed implies pending_forward_state, otherwise it must be None
- pending_forward_state: Option<PendingHTLCStatus>,
}
-#[derive(PartialEq)]
enum OutboundHTLCState {
/// Added by us and included in a commitment_signed (if we were AwaitingRemoteRevoke when we
/// created it we would have put it in the holding cell instead). When they next revoke_and_ack
/// allowed to remove it, the "can only be removed once committed on both sides" requirement
/// doesn't matter to us and its up to them to enforce it, worst-case they jump ahead but
/// we'll never get out of sync).
- LocalAnnounced,
+ /// Note that we Box the OnionPacket as its rather large and we don't want to blow up
+ /// OutboundHTLCOutput's size just for a temporary bit
+ LocalAnnounced(Box<msgs::OnionPacket>),
Committed,
/// Remote removed this (outbound) HTLC. We're waiting on their commitment_signed to finalize
/// the change (though they'll need to revoke before we fail the payment).
ShutdownComplete = 2048,
}
const BOTH_SIDES_SHUTDOWN_MASK: u32 = (ChannelState::LocalShutdownSent as u32 | ChannelState::RemoteShutdownSent as u32);
+const MULTI_STATE_FLAGS: u32 = (BOTH_SIDES_SHUTDOWN_MASK | ChannelState::PeerDisconnected as u32);
const INITIAL_COMMITMENT_NUMBER: u64 = (1 << 48) - 1;
pending_inbound_htlcs: Vec<InboundHTLCOutput>,
pending_outbound_htlcs: Vec<OutboundHTLCOutput>,
holding_cell_htlc_updates: Vec<HTLCUpdateAwaitingACK>,
+
+ // pending_update_fee is filled when sending and receiving update_fee
+ // For outbound channel, feerate_per_kw is updated with the value from
+ // pending_update_fee when revoke_and_ack is received
+ //
+ // For inbound channel, feerate_per_kw is updated when it receives
+ // commitment_signed and revoke_and_ack is generated
+ // The pending value is kept when another pair of update_fee and commitment_signed
+ // is received during AwaitingRemoteRevoke and relieved when the expected
+ // revoke_and_ack is received and new commitment_signed is generated to be
+ // sent to the funder. Otherwise, the pending value is removed when receiving
+ // commitment_signed.
+ pending_update_fee: Option<u64>,
+ // update_fee() during ChannelState::AwaitingRemoteRevoke is hold in
+ // holdina_cell_update_fee then moved to pending_udpate_fee when revoke_and_ack
+ // is received. holding_cell_update_fee is updated when there are additional
+ // update_fee() during ChannelState::AwaitingRemoteRevoke.
+ holding_cell_update_fee: Option<u64>,
next_local_htlc_id: u64,
next_remote_htlc_id: u64,
channel_update_count: u32,
feerate_per_kw: u64,
+ #[cfg(debug_assertions)]
+ /// Max to_local and to_remote outputs in a locally-generated commitment transaction
+ max_commitment_tx_output_local: ::std::sync::Mutex<(u64, u64)>,
+ #[cfg(debug_assertions)]
+ /// Max to_local and to_remote outputs in a remote-generated commitment transaction
+ max_commitment_tx_output_remote: ::std::sync::Mutex<(u64, u64)>,
+
#[cfg(test)]
// Used in ChannelManager's tests to send a revoked transaction
pub last_local_commitment_txn: Vec<Transaction>,
our_dust_limit_satoshis: u64,
their_max_htlc_value_in_flight_msat: u64,
//get_our_max_htlc_value_in_flight_msat(): u64,
+ /// minimum channel reserve for **self** to maintain - set by them.
their_channel_reserve_satoshis: u64,
//get_our_channel_reserve_satoshis(): u64,
their_htlc_minimum_msat: u64,
/// it's 2^24.
pub const MAX_FUNDING_SATOSHIS: u64 = (1 << 24);
+/// Used to return a simple Error back to ChannelManager. Will get converted to a
+/// msgs::ErrorAction::SendErrorMessage or msgs::ErrorAction::IgnoreError as appropriate with our
+/// channel_id in ChannelManager.
+pub(super) enum ChannelError {
+ Ignore(&'static str),
+ Close(&'static str),
+}
+
macro_rules! secp_call {
( $res: expr, $err: expr, $chan_id: expr ) => {
match $res {
channel_value_satoshis * 1000 / 10 //TODO
}
+ /// Returns a minimum channel reserve value **they** need to maintain
+ ///
/// Guaranteed to return a value no larger than channel_value_satoshis
fn get_our_channel_reserve_satoshis(channel_value_satoshis: u64) -> u64 {
let (q, _) = channel_value_satoshis.overflowing_div(100);
pending_inbound_htlcs: Vec::new(),
pending_outbound_htlcs: Vec::new(),
holding_cell_htlc_updates: Vec::new(),
+ pending_update_fee: None,
+ holding_cell_update_fee: None,
next_local_htlc_id: 0,
next_remote_htlc_id: 0,
channel_update_count: 1,
+ #[cfg(debug_assertions)]
+ max_commitment_tx_output_local: ::std::sync::Mutex::new((channel_value_satoshis * 1000 - push_msat, push_msat)),
+ #[cfg(debug_assertions)]
+ max_commitment_tx_output_remote: ::std::sync::Mutex::new((channel_value_satoshis * 1000 - push_msat, push_msat)),
+
last_local_commitment_txn: Vec::new(),
last_sent_closing_fee: None,
})
}
- fn check_remote_fee(fee_estimator: &FeeEstimator, feerate_per_kw: u32) -> Result<(), HandleError> {
+ fn check_remote_fee(fee_estimator: &FeeEstimator, feerate_per_kw: u32) -> Result<(), ChannelError> {
if (feerate_per_kw as u64) < fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background) {
- return Err(HandleError{err: "Peer's feerate much too low", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
+ return Err(ChannelError::Close("Peer's feerate much too low"));
}
if (feerate_per_kw as u64) > fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::HighPriority) * 2 {
- return Err(HandleError{err: "Peer's feerate much too high", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
+ return Err(ChannelError::Close("Peer's feerate much too high"));
}
Ok(())
}
/// Creates a new channel from a remote sides' request for one.
/// Assumes chain_hash has already been checked and corresponds with what we expect!
- /// Generally prefers to take the DisconnectPeer action on failure, as a notice to the sender
- /// that we're rejecting the new channel.
- pub fn new_from_req(fee_estimator: &FeeEstimator, chan_keys: ChannelKeys, their_node_id: PublicKey, msg: &msgs::OpenChannel, user_id: u64, require_announce: bool, allow_announce: bool, logger: Arc<Logger>) -> Result<Channel, HandleError> {
- macro_rules! return_error_message {
- ( $msg: expr ) => {
- return Err(HandleError{err: $msg, action: Some(msgs::ErrorAction::SendErrorMessage{ msg: msgs::ErrorMessage { channel_id: msg.temporary_channel_id, data: $msg.to_string() }})});
- }
- }
-
+ pub fn new_from_req(fee_estimator: &FeeEstimator, chan_keys: ChannelKeys, their_node_id: PublicKey, msg: &msgs::OpenChannel, user_id: u64, require_announce: bool, allow_announce: bool, logger: Arc<Logger>) -> Result<Channel, ChannelError> {
// Check sanity of message fields:
if msg.funding_satoshis >= MAX_FUNDING_SATOSHIS {
- return_error_message!("funding value > 2^24");
+ return Err(ChannelError::Close("funding value > 2^24"));
}
if msg.channel_reserve_satoshis > msg.funding_satoshis {
- return_error_message!("Bogus channel_reserve_satoshis");
+ return Err(ChannelError::Close("Bogus channel_reserve_satoshis"));
}
if msg.push_msat > (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000 {
- return_error_message!("push_msat larger than funding value");
+ return Err(ChannelError::Close("push_msat larger than funding value"));
}
if msg.dust_limit_satoshis > msg.funding_satoshis {
- return_error_message!("Peer never wants payout outputs?");
+ return Err(ChannelError::Close("Peer never wants payout outputs?"));
}
if msg.dust_limit_satoshis > msg.channel_reserve_satoshis {
- return_error_message!("Bogus; channel reserve is less than dust limit");
+ return Err(ChannelError::Close("Bogus; channel reserve is less than dust limit"));
}
if msg.htlc_minimum_msat >= (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000 {
- return_error_message!("Miminum htlc value is full channel value");
+ return Err(ChannelError::Close("Miminum htlc value is full channel value"));
}
- Channel::check_remote_fee(fee_estimator, msg.feerate_per_kw).map_err(|e|
- HandleError{err: e.err, action: Some(msgs::ErrorAction::SendErrorMessage{ msg: msgs::ErrorMessage { channel_id: msg.temporary_channel_id, data: e.err.to_string() }})}
- )?;
+ Channel::check_remote_fee(fee_estimator, msg.feerate_per_kw)?;
if msg.to_self_delay > MAX_LOCAL_BREAKDOWN_TIMEOUT {
- return_error_message!("They wanted our payments to be delayed by a needlessly long period");
+ return Err(ChannelError::Close("They wanted our payments to be delayed by a needlessly long period"));
}
if msg.max_accepted_htlcs < 1 {
- return_error_message!("0 max_accpted_htlcs makes for a useless channel");
+ return Err(ChannelError::Close("0 max_accpted_htlcs makes for a useless channel"));
}
if msg.max_accepted_htlcs > 483 {
- return_error_message!("max_accpted_htlcs > 483");
+ return Err(ChannelError::Close("max_accpted_htlcs > 483"));
}
// Convert things into internal flags and prep our state:
let their_announce = if (msg.channel_flags & 1) == 1 { true } else { false };
if require_announce && !their_announce {
- return_error_message!("Peer tried to open unannounced channel, but we require public ones");
+ return Err(ChannelError::Close("Peer tried to open unannounced channel, but we require public ones"));
}
if !allow_announce && their_announce {
- return_error_message!("Peer tried to open announced channel, but we require private ones");
+ return Err(ChannelError::Close("Peer tried to open announced channel, but we require private ones"));
}
let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
let our_dust_limit_satoshis = Channel::derive_our_dust_limit_satoshis(background_feerate);
let our_channel_reserve_satoshis = Channel::get_our_channel_reserve_satoshis(msg.funding_satoshis);
if our_channel_reserve_satoshis < our_dust_limit_satoshis {
- return_error_message!("Suitalbe channel reserve not found. aborting");
+ return Err(ChannelError::Close("Suitalbe channel reserve not found. aborting"));
}
if msg.channel_reserve_satoshis < our_dust_limit_satoshis {
- return_error_message!("channel_reserve_satoshis too small");
+ return Err(ChannelError::Close("channel_reserve_satoshis too small"));
}
if our_channel_reserve_satoshis < msg.dust_limit_satoshis {
- return_error_message!("Dust limit too high for our channel reserve");
+ return Err(ChannelError::Close("Dust limit too high for our channel reserve"));
}
// check if the funder's amount for the initial commitment tx is sufficient
// for full fee payment
let funders_amount_msat = msg.funding_satoshis * 1000 - msg.push_msat;
if funders_amount_msat < background_feerate * COMMITMENT_TX_BASE_WEIGHT {
- return_error_message!("Insufficient funding amount for initial commitment");
+ return Err(ChannelError::Close("Insufficient funding amount for initial commitment"));
}
let to_local_msat = msg.push_msat;
let to_remote_msat = funders_amount_msat - background_feerate * COMMITMENT_TX_BASE_WEIGHT;
if to_local_msat <= msg.channel_reserve_satoshis * 1000 && to_remote_msat <= our_channel_reserve_satoshis * 1000 {
- return_error_message!("Insufficient funding amount for initial commitment");
+ return Err(ChannelError::Close("Insufficient funding amount for initial commitment"));
}
let secp_ctx = Secp256k1::new();
pending_inbound_htlcs: Vec::new(),
pending_outbound_htlcs: Vec::new(),
holding_cell_htlc_updates: Vec::new(),
+ pending_update_fee: None,
+ holding_cell_update_fee: None,
next_local_htlc_id: 0,
next_remote_htlc_id: 0,
channel_update_count: 1,
+ #[cfg(debug_assertions)]
+ max_commitment_tx_output_local: ::std::sync::Mutex::new((msg.push_msat, msg.funding_satoshis * 1000 - msg.push_msat)),
+ #[cfg(debug_assertions)]
+ max_commitment_tx_output_remote: ::std::sync::Mutex::new((msg.push_msat, msg.funding_satoshis * 1000 - msg.push_msat)),
+
last_local_commitment_txn: Vec::new(),
last_sent_closing_fee: None,
/// 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) -> (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>) {
let obscured_commitment_transaction_number = self.get_commitment_transaction_number_obscure_factor() ^ (INITIAL_COMMITMENT_NUMBER - commitment_number);
let txins = {
macro_rules! add_htlc_output {
($htlc: expr, $outbound: expr) => {
if $outbound == local { // "offered HTLC output"
- if $htlc.amount_msat / 1000 >= dust_limit_satoshis + (self.feerate_per_kw * HTLC_TIMEOUT_TX_WEIGHT / 1000) {
+ 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(),
}, Some(htlc_in_tx)));
}
} else {
- if $htlc.amount_msat / 1000 >= dust_limit_satoshis + (self.feerate_per_kw * HTLC_SUCCESS_TX_WEIGHT / 1000) {
+ if $htlc.amount_msat / 1000 >= dust_limit_satoshis + (feerate_per_kw * HTLC_SUCCESS_TX_WEIGHT / 1000) {
let htlc_in_tx = get_htlc_in_commitment!($htlc, false);
txouts.push((TxOut { // "received HTLC output"
script_pubkey: chan_utils::get_htlc_redeemscript(&htlc_in_tx, &keys).to_v0_p2wsh(),
for ref htlc in self.pending_inbound_htlcs.iter() {
let include = match htlc.state {
- InboundHTLCState::RemoteAnnounced => !generated_by_local,
- InboundHTLCState::AwaitingRemoteRevokeToAnnounce => !generated_by_local,
- InboundHTLCState::AwaitingAnnouncedRemoteRevoke => true,
+ InboundHTLCState::RemoteAnnounced(_) => !generated_by_local,
+ InboundHTLCState::AwaitingRemoteRevokeToAnnounce(_) => !generated_by_local,
+ InboundHTLCState::AwaitingAnnouncedRemoteRevoke(_) => true,
InboundHTLCState::Committed => true,
- InboundHTLCState::LocalRemoved => !generated_by_local,
+ InboundHTLCState::LocalRemoved(_) => !generated_by_local,
};
if include {
add_htlc_output!(htlc, false);
remote_htlc_total_msat += htlc.amount_msat;
} else {
- match htlc.state {
- InboundHTLCState::LocalRemoved => {
- if generated_by_local && htlc.local_removed_fulfilled {
- value_to_self_msat_offset += htlc.amount_msat as i64;
+ match &htlc.state {
+ &InboundHTLCState::LocalRemoved(ref reason) => {
+ if generated_by_local {
+ if let &InboundHTLCRemovalReason::Fulfill(_) = reason {
+ value_to_self_msat_offset += htlc.amount_msat as i64;
+ }
}
},
_ => {},
for ref htlc in self.pending_outbound_htlcs.iter() {
let include = match htlc.state {
- OutboundHTLCState::LocalAnnounced => generated_by_local,
+ OutboundHTLCState::LocalAnnounced(_) => generated_by_local,
OutboundHTLCState::Committed => true,
OutboundHTLCState::RemoteRemoved => generated_by_local,
OutboundHTLCState::AwaitingRemoteRevokeToRemove => generated_by_local,
}
}
- let total_fee: u64 = self.feerate_per_kw * (COMMITMENT_TX_BASE_WEIGHT + (txouts.len() as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
- let value_to_self: i64 = ((self.value_to_self_msat - local_htlc_total_msat) as i64 + value_to_self_msat_offset) / 1000 - if self.channel_outbound { total_fee as i64 } else { 0 };
- let value_to_remote: i64 = (((self.channel_value_satoshis * 1000 - self.value_to_self_msat - remote_htlc_total_msat) as i64 - value_to_self_msat_offset) / 1000) - if self.channel_outbound { 0 } else { total_fee as i64 };
+
+ let value_to_self_msat: i64 = (self.value_to_self_msat - local_htlc_total_msat) as i64 + value_to_self_msat_offset;
+ let value_to_remote_msat: i64 = (self.channel_value_satoshis * 1000 - self.value_to_self_msat - remote_htlc_total_msat) as i64 - value_to_self_msat_offset;
+
+ #[cfg(debug_assertions)]
+ {
+ // Make sure that the to_self/to_remote is always either past the appropriate
+ // channel_reserve *or* it is making progress towards it.
+ // TODO: This should happen after fee calculation, but we don't handle that correctly
+ // yet!
+ let mut max_commitment_tx_output = if generated_by_local {
+ self.max_commitment_tx_output_local.lock().unwrap()
+ } else {
+ self.max_commitment_tx_output_remote.lock().unwrap()
+ };
+ debug_assert!(max_commitment_tx_output.0 <= value_to_self_msat as u64 || value_to_self_msat / 1000 >= self.their_channel_reserve_satoshis as i64);
+ max_commitment_tx_output.0 = cmp::max(max_commitment_tx_output.0, value_to_self_msat as u64);
+ debug_assert!(max_commitment_tx_output.1 <= value_to_remote_msat as u64 || value_to_remote_msat / 1000 >= Channel::get_our_channel_reserve_satoshis(self.channel_value_satoshis) as i64);
+ max_commitment_tx_output.1 = cmp::max(max_commitment_tx_output.1, value_to_remote_msat as u64);
+ }
+
+ let total_fee: u64 = feerate_per_kw * (COMMITMENT_TX_BASE_WEIGHT + (txouts.len() as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
+ let (value_to_self, value_to_remote) = if self.channel_outbound {
+ (value_to_self_msat / 1000 - total_fee as i64, value_to_remote_msat / 1000)
+ } else {
+ (value_to_self_msat / 1000, value_to_remote_msat / 1000 - total_fee as i64)
+ };
let value_to_a = if local { value_to_self } else { value_to_remote };
let value_to_b = if local { value_to_remote } else { value_to_self };
/// Builds the htlc-success or htlc-timeout transaction which spends a given HTLC output
/// @local is used only to convert relevant internal structures which refer to remote vs local
/// to decide value of outputs and direction of HTLCs.
- fn build_htlc_transaction(&self, prev_hash: &Sha256dHash, htlc: &HTLCOutputInCommitment, local: bool, keys: &TxCreationKeys) -> Transaction {
- chan_utils::build_htlc_transaction(prev_hash, self.feerate_per_kw, if local { self.their_to_self_delay } else { BREAKDOWN_TIMEOUT }, htlc, &keys.a_delayed_payment_key, &keys.revocation_key)
+ fn build_htlc_transaction(&self, prev_hash: &Sha256dHash, htlc: &HTLCOutputInCommitment, local: bool, keys: &TxCreationKeys, feerate_per_kw: u64) -> Transaction {
+ chan_utils::build_htlc_transaction(prev_hash, feerate_per_kw, if local { self.their_to_self_delay } else { BREAKDOWN_TIMEOUT }, 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), HandleError> {
for (idx, htlc) in self.pending_inbound_htlcs.iter().enumerate() {
if htlc.htlc_id == htlc_id_arg {
assert_eq!(htlc.payment_hash, payment_hash_calc);
- if htlc.state != InboundHTLCState::Committed {
+ if let InboundHTLCState::Committed = htlc.state {
+ } else {
debug_assert!(false, "Have an inbound HTLC we tried to claim before it was fully committed to");
// Don't return in release mode here so that we can update channel_monitor
}
{
let htlc = &mut self.pending_inbound_htlcs[pending_idx];
- if htlc.state != InboundHTLCState::Committed {
+ if let InboundHTLCState::Committed = htlc.state {
+ } else {
debug_assert!(false, "Have an inbound HTLC we tried to claim before it was fully committed to");
return Ok((None, Some(self.channel_monitor.clone())));
}
- htlc.state = InboundHTLCState::LocalRemoved;
- htlc.local_removed_fulfilled = true;
+ htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(payment_preimage_arg.clone()));
}
Ok((Some(msgs::UpdateFulfillHTLC {
let mut pending_idx = std::usize::MAX;
for (idx, htlc) in self.pending_inbound_htlcs.iter().enumerate() {
if htlc.htlc_id == htlc_id_arg {
- if htlc.state != InboundHTLCState::Committed {
+ if let InboundHTLCState::Committed = htlc.state {
+ } else {
debug_assert!(false, "Have an inbound HTLC we tried to fail before it was fully committed to");
return Err(HandleError{err: "Unable to find a pending HTLC which matched the given HTLC ID", action: Some(msgs::ErrorAction::IgnoreError)});
}
{
let htlc = &mut self.pending_inbound_htlcs[pending_idx];
- htlc.state = InboundHTLCState::LocalRemoved;
- htlc.local_removed_fulfilled = false;
+ htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(err_packet.clone()));
}
Ok(Some(msgs::UpdateFailHTLC {
// Message handlers:
- pub fn accept_channel(&mut self, msg: &msgs::AcceptChannel) -> Result<(), HandleError> {
- macro_rules! return_error_message {
- ( $msg: expr ) => {
- return Err(HandleError{err: $msg, action: Some(msgs::ErrorAction::SendErrorMessage{ msg: msgs::ErrorMessage { channel_id: msg.temporary_channel_id, data: $msg.to_string() }})});
- }
- }
+ pub fn accept_channel(&mut self, msg: &msgs::AcceptChannel) -> Result<(), ChannelError> {
// Check sanity of message fields:
if !self.channel_outbound {
- return_error_message!("Got an accept_channel message from an inbound peer");
+ return Err(ChannelError::Close("Got an accept_channel message from an inbound peer"));
}
if self.channel_state != ChannelState::OurInitSent as u32 {
- return_error_message!("Got an accept_channel message at a strange time");
+ return Err(ChannelError::Close("Got an accept_channel message at a strange time"));
}
if msg.dust_limit_satoshis > 21000000 * 100000000 {
- return_error_message!("Peer never wants payout outputs?");
+ return Err(ChannelError::Close("Peer never wants payout outputs?"));
}
if msg.channel_reserve_satoshis > self.channel_value_satoshis {
- return_error_message!("Bogus channel_reserve_satoshis");
+ return Err(ChannelError::Close("Bogus channel_reserve_satoshis"));
}
if msg.dust_limit_satoshis > msg.channel_reserve_satoshis {
- return_error_message!("Bogus channel_reserve and dust_limit");
+ return Err(ChannelError::Close("Bogus channel_reserve and dust_limit"));
}
if msg.channel_reserve_satoshis < self.our_dust_limit_satoshis {
- return_error_message!("Peer never wants payout outputs?");
+ return Err(ChannelError::Close("Peer never wants payout outputs?"));
}
if msg.dust_limit_satoshis > Channel::get_our_channel_reserve_satoshis(self.channel_value_satoshis) {
- return_error_message!("Dust limit is bigger than our channel reverse");
+ return Err(ChannelError::Close("Dust limit is bigger than our channel reverse"));
}
if msg.htlc_minimum_msat >= (self.channel_value_satoshis - msg.channel_reserve_satoshis) * 1000 {
- return_error_message!("Minimum htlc value is full channel value");
+ return Err(ChannelError::Close("Minimum htlc value is full channel value"));
}
if msg.minimum_depth > Channel::derive_maximum_minimum_depth(self.channel_value_satoshis*1000, self.value_to_self_msat) {
- return_error_message!("minimum_depth too large");
+ return Err(ChannelError::Close("minimum_depth too large"));
}
if msg.to_self_delay > MAX_LOCAL_BREAKDOWN_TIMEOUT {
- return_error_message!("They wanted our payments to be delayed by a needlessly long period");
+ return Err(ChannelError::Close("They wanted our payments to be delayed by a needlessly long period"));
}
if msg.max_accepted_htlcs < 1 {
- return_error_message!("0 max_accpted_htlcs makes for a useless channel");
+ return Err(ChannelError::Close("0 max_accpted_htlcs makes for a useless channel"));
}
if msg.max_accepted_htlcs > 483 {
- return_error_message!("max_accpted_htlcs > 483");
+ return Err(ChannelError::Close("max_accpted_htlcs > 483"));
}
// TODO: Optional additional constraints mentioned in the spec
let funding_script = self.get_funding_redeemscript();
let local_keys = self.build_local_transaction_keys(self.cur_local_commitment_transaction_number)?;
- let local_initial_commitment_tx = self.build_commitment_transaction(self.cur_local_commitment_transaction_number, &local_keys, true, false).0;
+ let local_initial_commitment_tx = self.build_commitment_transaction(self.cur_local_commitment_transaction_number, &local_keys, true, false, self.feerate_per_kw).0;
let local_sighash = Message::from_slice(&bip143::SighashComponents::new(&local_initial_commitment_tx).sighash_all(&local_initial_commitment_tx.input[0], &funding_script, self.channel_value_satoshis)[..]).unwrap();
// They sign the "local" commitment transaction, allowing us to broadcast the tx if we wish.
secp_call!(self.secp_ctx.verify(&local_sighash, &sig, &self.their_funding_pubkey.unwrap()), "Invalid funding_created signature from peer", self.channel_id());
let remote_keys = self.build_remote_transaction_keys()?;
- let remote_initial_commitment_tx = self.build_commitment_transaction(self.cur_remote_commitment_transaction_number, &remote_keys, false, false).0;
+ let remote_initial_commitment_tx = self.build_commitment_transaction(self.cur_remote_commitment_transaction_number, &remote_keys, false, false, self.feerate_per_kw).0;
let remote_sighash = Message::from_slice(&bip143::SighashComponents::new(&remote_initial_commitment_tx).sighash_all(&remote_initial_commitment_tx.input[0], &funding_script, self.channel_value_satoshis)[..]).unwrap();
// We sign the "remote" commitment transaction, allowing them to broadcast the tx if they wish.
let funding_script = self.get_funding_redeemscript();
let local_keys = self.build_local_transaction_keys(self.cur_local_commitment_transaction_number)?;
- let mut local_initial_commitment_tx = self.build_commitment_transaction(self.cur_local_commitment_transaction_number, &local_keys, true, false).0;
+ let mut local_initial_commitment_tx = self.build_commitment_transaction(self.cur_local_commitment_transaction_number, &local_keys, true, false, self.feerate_per_kw).0;
let local_sighash = Message::from_slice(&bip143::SighashComponents::new(&local_initial_commitment_tx).sighash_all(&local_initial_commitment_tx.input[0], &funding_script, self.channel_value_satoshis)[..]).unwrap();
// They sign the "local" commitment transaction, allowing us to broadcast the tx if we wish.
Ok(self.channel_monitor.clone())
}
- pub fn funding_locked(&mut self, msg: &msgs::FundingLocked) -> Result<(), HandleError> {
+ pub fn funding_locked(&mut self, msg: &msgs::FundingLocked) -> Result<(), ChannelError> {
if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
- return Err(HandleError{err: "Peer sent funding_locked when we needed a channel_reestablish", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer sent funding_locked when we needed a channel_reestablish".to_string(), channel_id: msg.channel_id}})});
+ return Err(ChannelError::Close("Peer sent funding_locked when we needed a channel_reestablish"));
}
let non_shutdown_state = self.channel_state & (!BOTH_SIDES_SHUTDOWN_MASK);
if non_shutdown_state == ChannelState::FundingSent as u32 {
self.cur_local_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 1 &&
self.cur_remote_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 1 {
if self.their_cur_commitment_point != Some(msg.next_per_commitment_point) {
- return Err(HandleError{err: "Peer sent a reconnect funding_locked with a different point", action: None});
+ return Err(ChannelError::Close("Peer sent a reconnect funding_locked with a different point"));
}
// They probably disconnected/reconnected and re-sent the funding_locked, which is required
return Ok(());
} else {
- return Err(HandleError{err: "Peer sent a funding_locked at a strange time", action: None});
+ return Err(ChannelError::Close("Peer sent a funding_locked at a strange time"));
}
self.their_prev_commitment_point = self.their_cur_commitment_point;
Ok(())
}
- /// Returns (inbound_htlc_count, outbound_htlc_count, htlc_outbound_value_msat, htlc_inbound_value_msat)
- /// If its for a remote update check, we need to be more lax about checking against messages we
- /// sent but they may not have received/processed before they sent this message. Further, for
- /// our own sends, we're more conservative and even consider things they've removed against
- /// totals, though there is little reason to outside of further avoiding any race condition
- /// issues.
- fn get_pending_htlc_stats(&self, for_remote_update_check: bool) -> (u32, u32, u64, u64) {
- //TODO: Can probably split this into inbound/outbound
- let mut inbound_htlc_count: u32 = 0;
- let mut outbound_htlc_count: u32 = 0;
- let mut htlc_outbound_value_msat = 0;
+ /// Returns (inbound_htlc_count, htlc_inbound_value_msat)
+ fn get_inbound_pending_htlc_stats(&self) -> (u32, u64) {
let mut htlc_inbound_value_msat = 0;
for ref htlc in self.pending_inbound_htlcs.iter() {
- match htlc.state {
- InboundHTLCState::RemoteAnnounced => {},
- InboundHTLCState::AwaitingRemoteRevokeToAnnounce => {},
- InboundHTLCState::AwaitingAnnouncedRemoteRevoke => {},
- InboundHTLCState::Committed => {},
- InboundHTLCState::LocalRemoved => {},
- }
- inbound_htlc_count += 1;
htlc_inbound_value_msat += htlc.amount_msat;
}
+ (self.pending_inbound_htlcs.len() as u32, htlc_inbound_value_msat)
+ }
+
+ /// Returns (outbound_htlc_count, htlc_outbound_value_msat)
+ fn get_outbound_pending_htlc_stats(&self) -> (u32, u64) {
+ let mut htlc_outbound_value_msat = 0;
for ref htlc in self.pending_outbound_htlcs.iter() {
- match htlc.state {
- OutboundHTLCState::LocalAnnounced => { if for_remote_update_check { continue; } },
- OutboundHTLCState::Committed => {},
- OutboundHTLCState::RemoteRemoved => { if for_remote_update_check { continue; } },
- OutboundHTLCState::AwaitingRemoteRevokeToRemove => { if for_remote_update_check { continue; } },
- OutboundHTLCState::AwaitingRemovedRemoteRevoke => { if for_remote_update_check { continue; } },
- }
- outbound_htlc_count += 1;
htlc_outbound_value_msat += htlc.amount_msat;
}
- (inbound_htlc_count, outbound_htlc_count, htlc_outbound_value_msat, htlc_inbound_value_msat)
+ (self.pending_outbound_htlcs.len() as u32, htlc_outbound_value_msat)
}
pub fn update_add_htlc(&mut self, msg: &msgs::UpdateAddHTLC, pending_forward_state: PendingHTLCStatus) -> Result<(), HandleError> {
return Err(HandleError{err: "Remote side tried to send less than our minimum HTLC value", action: None});
}
- let (inbound_htlc_count, _, htlc_outbound_value_msat, htlc_inbound_value_msat) = self.get_pending_htlc_stats(true);
+ let (inbound_htlc_count, htlc_inbound_value_msat) = self.get_inbound_pending_htlc_stats();
if inbound_htlc_count + 1 > OUR_MAX_HTLCS as u32 {
return Err(HandleError{err: "Remote tried to push more than our max accepted HTLCs", action: None});
}
// Check our_channel_reserve_satoshis (we're getting paid, so they have to at least meet
// the reserve_satoshis we told them to always have as direct payment so that they lose
// something if we punish them for broadcasting an old state).
- if htlc_inbound_value_msat + htlc_outbound_value_msat + msg.amount_msat + self.value_to_self_msat > (self.channel_value_satoshis - Channel::get_our_channel_reserve_satoshis(self.channel_value_satoshis)) * 1000 {
+ if htlc_inbound_value_msat + msg.amount_msat + self.value_to_self_msat > (self.channel_value_satoshis - Channel::get_our_channel_reserve_satoshis(self.channel_value_satoshis)) * 1000 {
return Err(HandleError{err: "Remote HTLC add would put them over their reserve value", action: None});
}
if self.next_remote_htlc_id != msg.htlc_id {
amount_msat: msg.amount_msat,
payment_hash: msg.payment_hash,
cltv_expiry: msg.cltv_expiry,
- state: InboundHTLCState::RemoteAnnounced,
- local_removed_fulfilled: false,
- pending_forward_state: Some(pending_forward_state),
+ state: InboundHTLCState::RemoteAnnounced(pending_forward_state),
});
Ok(())
/// Removes an outbound HTLC which has been commitment_signed by the remote end
#[inline]
- fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, HandleError> {
+ fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option<HTLCFailReason>) -> Result<&HTLCSource, ChannelError> {
for htlc in self.pending_outbound_htlcs.iter_mut() {
if htlc.htlc_id == htlc_id {
match check_preimage {
None => {},
Some(payment_hash) =>
if payment_hash != htlc.payment_hash {
- return Err(HandleError{err: "Remote tried to fulfill HTLC with an incorrect preimage", action: None});
+ return Err(ChannelError::Close("Remote tried to fulfill HTLC with an incorrect preimage"));
}
};
match htlc.state {
- OutboundHTLCState::LocalAnnounced =>
- return Err(HandleError{err: "Remote tried to fulfill HTLC before it had been committed", action: None}),
+ OutboundHTLCState::LocalAnnounced(_) =>
+ return Err(ChannelError::Close("Remote tried to fulfill HTLC before it had been committed")),
OutboundHTLCState::Committed => {
htlc.state = OutboundHTLCState::RemoteRemoved;
htlc.fail_reason = fail_reason;
},
OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved =>
- return Err(HandleError{err: "Remote tried to fulfill HTLC that they'd already fulfilled", action: None}),
+ return Err(ChannelError::Close("Remote tried to fulfill HTLC that they'd already fulfilled")),
}
return Ok(&htlc.source);
}
}
- Err(HandleError{err: "Remote tried to fulfill/fail an HTLC we couldn't find", action: None})
+ Err(ChannelError::Close("Remote tried to fulfill/fail an HTLC we couldn't find"))
}
- pub fn update_fulfill_htlc(&mut self, msg: &msgs::UpdateFulfillHTLC) -> Result<&HTLCSource, HandleError> {
+ pub fn update_fulfill_htlc(&mut self, msg: &msgs::UpdateFulfillHTLC) -> Result<&HTLCSource, ChannelError> {
if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) {
- return Err(HandleError{err: "Got add HTLC message when channel was not in an operational state", action: None});
+ return Err(ChannelError::Close("Got fulfill HTLC message when channel was not in an operational state"));
}
if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
- return Err(HandleError{err: "Peer sent update_fulfill_htlc when we needed a channel_reestablish", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer sent update_fulfill_htlc when we needed a channel_reestablish".to_string(), channel_id: msg.channel_id}})});
+ return Err(ChannelError::Close("Peer sent update_fulfill_htlc when we needed a channel_reestablish"));
}
let mut sha = Sha256::new();
self.mark_outbound_htlc_removed(msg.htlc_id, Some(payment_hash), None)
}
- pub fn update_fail_htlc(&mut self, msg: &msgs::UpdateFailHTLC, fail_reason: HTLCFailReason) -> Result<&HTLCSource, HandleError> {
+ pub fn update_fail_htlc(&mut self, msg: &msgs::UpdateFailHTLC, fail_reason: HTLCFailReason) -> Result<&HTLCSource, ChannelError> {
if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) {
- return Err(HandleError{err: "Got add HTLC message when channel was not in an operational state", action: None});
+ return Err(ChannelError::Close("Got fail HTLC message when channel was not in an operational state"));
}
if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
- return Err(HandleError{err: "Peer sent update_fail_htlc when we needed a channel_reestablish", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer sent update_fail_htlc when we needed a channel_reestablish".to_string(), channel_id: msg.channel_id}})});
+ return Err(ChannelError::Close("Peer sent update_fail_htlc when we needed a channel_reestablish"));
}
self.mark_outbound_htlc_removed(msg.htlc_id, None, Some(fail_reason))
}
- pub fn update_fail_malformed_htlc<'a>(&mut self, msg: &msgs::UpdateFailMalformedHTLC, fail_reason: HTLCFailReason) -> Result<&HTLCSource, HandleError> {
+ pub fn update_fail_malformed_htlc<'a>(&mut self, msg: &msgs::UpdateFailMalformedHTLC, fail_reason: HTLCFailReason) -> Result<&HTLCSource, ChannelError> {
if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) {
- return Err(HandleError{err: "Got add HTLC message when channel was not in an operational state", action: None});
+ return Err(ChannelError::Close("Got fail malformed HTLC message when channel was not in an operational state"));
}
if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
- return Err(HandleError{err: "Peer sent update_fail_malformed_htlc when we needed a channel_reestablish", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer sent update_fail_malformed_htlc when we needed a channel_reestablish".to_string(), channel_id: msg.channel_id}})});
+ return Err(ChannelError::Close("Peer sent update_fail_malformed_htlc when we needed a channel_reestablish"));
}
self.mark_outbound_htlc_removed(msg.htlc_id, None, Some(fail_reason))
let funding_script = self.get_funding_redeemscript();
let local_keys = self.build_local_transaction_keys(self.cur_local_commitment_transaction_number)?;
- let mut local_commitment_tx = self.build_commitment_transaction(self.cur_local_commitment_transaction_number, &local_keys, true, false);
+
+ let feerate_per_kw = if !self.channel_outbound && self.pending_update_fee.is_some() {
+ self.pending_update_fee.unwrap()
+ } else {
+ self.feerate_per_kw
+ };
+
+ let mut local_commitment_tx = self.build_commitment_transaction(self.cur_local_commitment_transaction_number, &local_keys, true, false, feerate_per_kw);
let local_commitment_txid = local_commitment_tx.0.txid();
let local_sighash = Message::from_slice(&bip143::SighashComponents::new(&local_commitment_tx.0).sighash_all(&local_commitment_tx.0.input[0], &funding_script, self.channel_value_satoshis)[..]).unwrap();
secp_call!(self.secp_ctx.verify(&local_sighash, &msg.signature, &self.their_funding_pubkey.unwrap()), "Invalid commitment tx signature from peer", self.channel_id());
let mut htlcs_and_sigs = Vec::with_capacity(local_commitment_tx.1.len());
for (idx, ref htlc) in local_commitment_tx.1.iter().enumerate() {
- let mut htlc_tx = self.build_htlc_transaction(&local_commitment_txid, htlc, true, &local_keys);
+ 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();
secp_call!(self.secp_ctx.verify(&htlc_sighash, &msg.htlc_signatures[idx], &local_keys.b_htlc_key), "Invalid HTLC tx siganture from peer", self.channel_id());
let per_commitment_secret = chan_utils::build_commitment_secret(self.local_keys.commitment_seed, self.cur_local_commitment_transaction_number + 1);
// Update state now that we've passed all the can-fail calls...
+ let mut need_our_commitment = false;
+ if !self.channel_outbound {
+ if let Some(fee_update) = self.pending_update_fee {
+ self.feerate_per_kw = fee_update;
+ // We later use the presence of pending_update_fee to indicate we should generate a
+ // commitment_signed upon receipt of revoke_and_ack, so we can only set it to None
+ // if we're not awaiting a revoke (ie will send a commitment_signed now).
+ if (self.channel_state & ChannelState::AwaitingRemoteRevoke as u32) == 0 {
+ need_our_commitment = true;
+ self.pending_update_fee = None;
+ }
+ }
+ }
+
self.channel_monitor.provide_latest_local_commitment_tx_info(local_commitment_tx.0, local_keys, self.feerate_per_kw, htlcs_and_sigs);
- let mut need_our_commitment = false;
for htlc in self.pending_inbound_htlcs.iter_mut() {
- if htlc.state == InboundHTLCState::RemoteAnnounced {
- htlc.state = InboundHTLCState::AwaitingRemoteRevokeToAnnounce;
+ let new_forward = if let &InboundHTLCState::RemoteAnnounced(ref forward_info) = &htlc.state {
+ Some(forward_info.clone())
+ } else { None };
+ if let Some(forward_info) = new_forward {
+ htlc.state = InboundHTLCState::AwaitingRemoteRevokeToAnnounce(forward_info);
need_our_commitment = true;
}
}
for htlc in self.pending_outbound_htlcs.iter_mut() {
- if htlc.state == OutboundHTLCState::RemoteRemoved {
+ if let OutboundHTLCState::RemoteRemoved = htlc.state {
htlc.state = OutboundHTLCState::AwaitingRemoteRevokeToRemove;
need_our_commitment = true;
}
/// Used to fulfill holding_cell_htlcs when we get a remote ack (or implicitly get it by them
/// fulfilling or failing the last pending HTLC)
fn free_holding_cell_htlcs(&mut self) -> Result<Option<(msgs::CommitmentUpdate, ChannelMonitor)>, HandleError> {
- if self.holding_cell_htlc_updates.len() != 0 {
+ if self.holding_cell_htlc_updates.len() != 0 || self.holding_cell_update_fee.is_some() {
let mut htlc_updates = Vec::new();
mem::swap(&mut htlc_updates, &mut self.holding_cell_htlc_updates);
let mut update_add_htlcs = Vec::with_capacity(htlc_updates.len());
//fail it back the route, if its a temporary issue we can ignore it...
match err {
None => {
- if update_add_htlcs.is_empty() && update_fulfill_htlcs.is_empty() && update_fail_htlcs.is_empty() {
+ if update_add_htlcs.is_empty() && update_fulfill_htlcs.is_empty() && update_fail_htlcs.is_empty() && self.holding_cell_update_fee.is_none() {
// This should never actually happen and indicates we got some Errs back
// from update_fulfill_htlc/update_fail_htlc, but we handle it anyway in
// case there is some strange way to hit duplicate HTLC removes.
return Ok(None);
}
+ let update_fee = if let Some(feerate) = self.holding_cell_update_fee {
+ self.pending_update_fee = self.holding_cell_update_fee.take();
+ Some(msgs::UpdateFee {
+ channel_id: self.channel_id,
+ feerate_per_kw: feerate as u32,
+ })
+ } else {
+ None
+ };
let (commitment_signed, monitor_update) = self.send_commitment_no_status_check()?;
Ok(Some((msgs::CommitmentUpdate {
update_add_htlcs,
update_fulfill_htlcs,
update_fail_htlcs,
update_fail_malformed_htlcs: Vec::new(),
+ update_fee: update_fee,
commitment_signed,
}, monitor_update)))
},
let mut value_to_self_msat_diff: i64 = 0;
// We really shouldnt have two passes here, but retain gives a non-mutable ref (Rust bug)
self.pending_inbound_htlcs.retain(|htlc| {
- if htlc.state == InboundHTLCState::LocalRemoved {
- if htlc.local_removed_fulfilled {
+ if let &InboundHTLCState::LocalRemoved(ref reason) = &htlc.state {
+ if let &InboundHTLCRemovalReason::Fulfill(_) = reason {
value_to_self_msat_diff += htlc.amount_msat as i64;
}
false
} else { true }
});
self.pending_outbound_htlcs.retain(|htlc| {
- if htlc.state == OutboundHTLCState::AwaitingRemovedRemoteRevoke {
+ if let OutboundHTLCState::AwaitingRemovedRemoteRevoke = htlc.state {
if let Some(reason) = htlc.fail_reason.clone() { // We really want take() here, but, again, non-mut ref :(
revoked_htlcs.push((htlc.source.clone(), htlc.payment_hash, reason));
} else {
} else { true }
});
for htlc in self.pending_inbound_htlcs.iter_mut() {
- if htlc.state == InboundHTLCState::AwaitingRemoteRevokeToAnnounce {
- htlc.state = InboundHTLCState::AwaitingAnnouncedRemoteRevoke;
- require_commitment = true;
- } else if htlc.state == InboundHTLCState::AwaitingAnnouncedRemoteRevoke {
- match htlc.pending_forward_state.take().unwrap() {
- PendingHTLCStatus::Fail(fail_msg) => {
- htlc.state = InboundHTLCState::LocalRemoved;
- require_commitment = true;
- match fail_msg {
- HTLCFailureMsg::Relay(msg) => update_fail_htlcs.push(msg),
- HTLCFailureMsg::Malformed(msg) => update_fail_malformed_htlcs.push(msg),
+ let swap = if let &InboundHTLCState::AwaitingRemoteRevokeToAnnounce(_) = &htlc.state {
+ true
+ } else if let &InboundHTLCState::AwaitingAnnouncedRemoteRevoke(_) = &htlc.state {
+ true
+ } else { false };
+ if swap {
+ let mut state = InboundHTLCState::Committed;
+ mem::swap(&mut state, &mut htlc.state);
+
+ if let InboundHTLCState::AwaitingRemoteRevokeToAnnounce(forward_info) = state {
+ htlc.state = InboundHTLCState::AwaitingAnnouncedRemoteRevoke(forward_info);
+ require_commitment = true;
+ } else if let InboundHTLCState::AwaitingAnnouncedRemoteRevoke(forward_info) = state {
+ match forward_info {
+ PendingHTLCStatus::Fail(fail_msg) => {
+ require_commitment = true;
+ match fail_msg {
+ HTLCFailureMsg::Relay(msg) => {
+ htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailRelay(msg.reason.clone()));
+ update_fail_htlcs.push(msg)
+ },
+ HTLCFailureMsg::Malformed(msg) => {
+ htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailMalformed((msg.sha256_of_onion, msg.failure_code)));
+ update_fail_malformed_htlcs.push(msg)
+ },
+ }
+ },
+ PendingHTLCStatus::Forward(forward_info) => {
+ to_forward_infos.push((forward_info, htlc.htlc_id));
+ htlc.state = InboundHTLCState::Committed;
}
- },
- PendingHTLCStatus::Forward(forward_info) => {
- to_forward_infos.push((forward_info, htlc.htlc_id));
- htlc.state = InboundHTLCState::Committed;
}
}
}
}
for htlc in self.pending_outbound_htlcs.iter_mut() {
- if htlc.state == OutboundHTLCState::LocalAnnounced {
+ if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
htlc.state = OutboundHTLCState::Committed;
- } else if htlc.state == OutboundHTLCState::AwaitingRemoteRevokeToRemove {
+ } else if let OutboundHTLCState::AwaitingRemoteRevokeToRemove = htlc.state {
htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke;
require_commitment = true;
}
}
self.value_to_self_msat = (self.value_to_self_msat as i64 + value_to_self_msat_diff) as u64;
+ if self.channel_outbound {
+ if let Some(feerate) = self.pending_update_fee.take() {
+ self.feerate_per_kw = feerate;
+ }
+ } else {
+ if let Some(feerate) = self.pending_update_fee {
+ // Because a node cannot send two commitment_signed's in a row without getting a
+ // revoke_and_ack from us (as it would otherwise not know the per_commitment_point
+ // it should use to create keys with) and because a node can't send a
+ // commitment_signed without changes, checking if the feerate is equal to the
+ // pending feerate update is sufficient to detect require_commitment.
+ if feerate == self.feerate_per_kw {
+ require_commitment = true;
+ self.pending_update_fee = None;
+ }
+ }
+ }
+
match self.free_holding_cell_htlcs()? {
Some(mut commitment_update) => {
commitment_update.0.update_fail_htlcs.reserve(update_fail_htlcs.len());
update_fulfill_htlcs: Vec::new(),
update_fail_htlcs,
update_fail_malformed_htlcs,
+ update_fee: None,
commitment_signed
}), to_forward_infos, revoked_htlcs, monitor_update))
} else {
}
}
}
+
+ }
+
+ /// Adds a pending update to this channel. See the doc for send_htlc for
+ /// further details on the optionness of the return value.
+ /// You MUST call send_commitment prior to any other calls on this Channel
+ fn send_update_fee(&mut self, feerate_per_kw: u64) -> Option<msgs::UpdateFee> {
+ if !self.channel_outbound {
+ panic!("Cannot send fee from inbound channel");
+ }
+ if !self.is_usable() {
+ panic!("Cannot update fee until channel is fully established and we haven't started shutting down");
+ }
+ if !self.is_live() {
+ panic!("Cannot update fee while peer is disconnected (ChannelManager should have caught this)");
+ }
+
+ if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32)) == (ChannelState::AwaitingRemoteRevoke as u32) {
+ self.holding_cell_update_fee = Some(feerate_per_kw);
+ return None;
+ }
+
+ debug_assert!(self.pending_update_fee.is_none());
+ self.pending_update_fee = Some(feerate_per_kw);
+
+ Some(msgs::UpdateFee {
+ channel_id: self.channel_id,
+ feerate_per_kw: feerate_per_kw as u32,
+ })
+ }
+
+ pub fn send_update_fee_and_commit(&mut self, feerate_per_kw: u64) -> Result<Option<(msgs::UpdateFee, msgs::CommitmentSigned, ChannelMonitor)>, HandleError> {
+ match self.send_update_fee(feerate_per_kw) {
+ Some(update_fee) => {
+ let (commitment_signed, monitor_update) = self.send_commitment_no_status_check()?;
+ Ok(Some((update_fee, commitment_signed, monitor_update)))
+ },
+ None => Ok(None)
+ }
}
/// Removes any uncommitted HTLCs, to be used on peer disconnection, including any pending
let mut inbound_drop_count = 0;
self.pending_inbound_htlcs.retain(|htlc| {
match htlc.state {
- InboundHTLCState::RemoteAnnounced => {
+ InboundHTLCState::RemoteAnnounced(_) => {
// They sent us an update_add_htlc but we never got the commitment_signed.
// We'll tell them what commitment_signed we're expecting next and they'll drop
// this HTLC accordingly
inbound_drop_count += 1;
false
},
- InboundHTLCState::AwaitingRemoteRevokeToAnnounce|InboundHTLCState::AwaitingAnnouncedRemoteRevoke => {
- // Same goes for AwaitingRemoteRevokeToRemove and AwaitingRemovedRemoteRevoke
+ InboundHTLCState::AwaitingRemoteRevokeToAnnounce(_)|InboundHTLCState::AwaitingAnnouncedRemoteRevoke(_) => {
// We received a commitment_signed updating this HTLC and (at least hopefully)
// sent a revoke_and_ack (which we can re-transmit) and have heard nothing
// in response to it yet, so don't touch it.
true
},
InboundHTLCState::Committed => true,
- InboundHTLCState::LocalRemoved => { // Same goes for LocalAnnounced
+ InboundHTLCState::LocalRemoved(_) => {
// We (hopefully) sent a commitment_signed updating this HTLC (which we can
// re-transmit if needed) and they may have even sent a revoke_and_ack back
// (that we missed). Keep this around for now and if they tell us they missed
});
for htlc in self.pending_outbound_htlcs.iter_mut() {
- if htlc.state == OutboundHTLCState::RemoteRemoved {
+ if let OutboundHTLCState::RemoteRemoved = htlc.state {
// They sent us an update to remove this but haven't yet sent the corresponding
// commitment_signed, we need to move it back to Committed and they can re-send
// the update upon reconnection.
outbound_drops
}
- pub fn update_fee(&mut self, fee_estimator: &FeeEstimator, msg: &msgs::UpdateFee) -> Result<(), HandleError> {
+ pub fn update_fee(&mut self, fee_estimator: &FeeEstimator, msg: &msgs::UpdateFee) -> Result<(), ChannelError> {
if self.channel_outbound {
- return Err(HandleError{err: "Non-funding remote tried to update channel fee", action: None});
+ return Err(ChannelError::Close("Non-funding remote tried to update channel fee"));
}
if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
- return Err(HandleError{err: "Peer sent update_fee when we needed a channel_reestablish", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer sent update_fee when we needed a channel_reestablish".to_string(), channel_id: msg.channel_id}})});
+ return Err(ChannelError::Close("Peer sent update_fee when we needed a channel_reestablish"));
}
Channel::check_remote_fee(fee_estimator, msg.feerate_per_kw)?;
+
+ self.pending_update_fee = Some(msg.feerate_per_kw as u64);
self.channel_update_count += 1;
- self.feerate_per_kw = msg.feerate_per_kw as u64;
Ok(())
}
/// May panic if some calls other than message-handling calls (which will all Err immediately)
/// have been called between remove_uncommitted_htlcs_and_mark_paused and this call.
- pub fn channel_reestablish(&mut self, msg: &msgs::ChannelReestablish) -> Result<(Option<msgs::FundingLocked>, Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, Option<ChannelMonitor>), HandleError> {
+ pub fn channel_reestablish(&mut self, msg: &msgs::ChannelReestablish) -> Result<(Option<msgs::FundingLocked>, Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, Option<ChannelMonitor>), ChannelError> {
if self.channel_state & (ChannelState::PeerDisconnected as u32) == 0 {
- return Err(HandleError{err: "Peer sent a loose channel_reestablish not after reconnect", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer sent a loose channel_reestablish not after reconnect".to_string(), channel_id: msg.channel_id}})});
+ // While BOLT 2 doesn't indicate explicitly we should error this channel here, it
+ // almost certainly indicates we are going to end up out-of-sync in some way, so we
+ // just close here instead of trying to recover.
+ return Err(ChannelError::Close("Peer sent a loose channel_reestablish not after reconnect"));
}
if msg.next_local_commitment_number == 0 || msg.next_local_commitment_number >= INITIAL_COMMITMENT_NUMBER ||
msg.next_remote_commitment_number == 0 || msg.next_remote_commitment_number >= INITIAL_COMMITMENT_NUMBER {
- return Err(HandleError{err: "Peer send garbage channel_reestablish", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer send garbage channel_reestablish".to_string(), channel_id: msg.channel_id}})});
+ return Err(ChannelError::Close("Peer sent a garbage channel_reestablish"));
}
// Go ahead and unmark PeerDisconnected as various calls we may make check for it (and all
// remaining cases either succeed or ErrorMessage-fail).
self.channel_state &= !(ChannelState::PeerDisconnected as u32);
- let mut required_revoke = None;
- if msg.next_remote_commitment_number == INITIAL_COMMITMENT_NUMBER - self.cur_local_commitment_transaction_number {
+ let required_revoke = if msg.next_remote_commitment_number == INITIAL_COMMITMENT_NUMBER - self.cur_local_commitment_transaction_number {
// Remote isn't waiting on any RevokeAndACK from us!
// Note that if we need to repeat our FundingLocked we'll do that in the next if block.
+ None
} else if msg.next_remote_commitment_number == (INITIAL_COMMITMENT_NUMBER - 1) - self.cur_local_commitment_transaction_number {
let next_per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &self.build_local_commitment_secret(self.cur_local_commitment_transaction_number));
let per_commitment_secret = chan_utils::build_commitment_secret(self.local_keys.commitment_seed, self.cur_local_commitment_transaction_number + 2);
- required_revoke = Some(msgs::RevokeAndACK {
+ Some(msgs::RevokeAndACK {
channel_id: self.channel_id,
per_commitment_secret,
next_per_commitment_point,
- });
+ })
} else {
- return Err(HandleError{err: "Peer attempted to reestablish channel with a very old local commitment transaction", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer attempted to reestablish channel with a very old remote commitment transaction".to_string(), channel_id: msg.channel_id}})});
- }
+ return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old local commitment transaction"));
+ };
- if msg.next_local_commitment_number == INITIAL_COMMITMENT_NUMBER - self.cur_remote_commitment_transaction_number {
- if msg.next_remote_commitment_number == INITIAL_COMMITMENT_NUMBER - self.cur_local_commitment_transaction_number {
- log_debug!(self, "Reconnected channel {} with no lost commitment txn", log_bytes!(self.channel_id()));
- if msg.next_local_commitment_number == 1 && msg.next_remote_commitment_number == 1 {
- let next_per_commitment_secret = self.build_local_commitment_secret(self.cur_local_commitment_transaction_number);
- let next_per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &next_per_commitment_secret);
- return Ok((Some(msgs::FundingLocked {
- channel_id: self.channel_id(),
- next_per_commitment_point: next_per_commitment_point,
- }), None, None, None));
- }
+ // We increment cur_remote_commitment_transaction_number only upon receipt of
+ // revoke_and_ack, not on sending commitment_signed, so we add one if have
+ // AwaitingRemoteRevoke set, which indicates we sent a commitment_signed but haven't gotten
+ // the corresponding revoke_and_ack back yet.
+ let our_next_remote_commitment_number = INITIAL_COMMITMENT_NUMBER - self.cur_remote_commitment_transaction_number + if (self.channel_state & ChannelState::AwaitingRemoteRevoke as u32) != 0 { 1 } else { 0 };
+
+ let resend_funding_locked = if msg.next_local_commitment_number == 1 && INITIAL_COMMITMENT_NUMBER - self.cur_local_commitment_transaction_number == 1 {
+ let next_per_commitment_secret = self.build_local_commitment_secret(self.cur_local_commitment_transaction_number);
+ let next_per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &next_per_commitment_secret);
+ Some(msgs::FundingLocked {
+ channel_id: self.channel_id(),
+ next_per_commitment_point: next_per_commitment_point,
+ })
+ } else { None };
+
+ if msg.next_local_commitment_number == our_next_remote_commitment_number {
+ if required_revoke.is_some() {
+ log_debug!(self, "Reconnected channel {} with only lost outbound RAA", log_bytes!(self.channel_id()));
+ } else {
+ log_debug!(self, "Reconnected channel {} with no loss", log_bytes!(self.channel_id()));
}
if (self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32)) == 0 {
match self.free_holding_cell_htlcs() {
Err(e) => {
if let &Some(msgs::ErrorAction::DisconnectPeer{msg: Some(_)}) = &e.action {
+ return Err(ChannelError::Close(e.err));
} else if let &Some(msgs::ErrorAction::SendErrorMessage{msg: _}) = &e.action {
+ return Err(ChannelError::Close(e.err));
} else {
panic!("Got non-channel-failing result from free_holding_cell_htlcs");
}
- return Err(e);
},
- Ok(Some((commitment_update, channel_monitor))) => return Ok((None, required_revoke, Some(commitment_update), Some(channel_monitor))),
- Ok(None) => return Ok((None, required_revoke, None, None)),
+ Ok(Some((commitment_update, channel_monitor))) => return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(channel_monitor))),
+ Ok(None) => return Ok((resend_funding_locked, required_revoke, None, None)),
}
} else {
- return Ok((None, required_revoke, None, None));
+ return Ok((resend_funding_locked, required_revoke, None, None));
+ }
+ } else if msg.next_local_commitment_number == our_next_remote_commitment_number - 1 {
+ if required_revoke.is_some() {
+ log_debug!(self, "Reconnected channel {} with lost outbound RAA and lost remote commitment tx", log_bytes!(self.channel_id()));
+ } else {
+ log_debug!(self, "Reconnected channel {} with only lost remote commitment tx", log_bytes!(self.channel_id()));
}
- } else if msg.next_local_commitment_number == (INITIAL_COMMITMENT_NUMBER - 1) - self.cur_remote_commitment_transaction_number {
- return Ok((None, required_revoke,
+ let mut update_add_htlcs = Vec::new();
+ let mut update_fulfill_htlcs = Vec::new();
+ let mut update_fail_htlcs = Vec::new();
+ let mut update_fail_malformed_htlcs = Vec::new();
+
+ for htlc in self.pending_outbound_htlcs.iter() {
+ if let &OutboundHTLCState::LocalAnnounced(ref onion_packet) = &htlc.state {
+ update_add_htlcs.push(msgs::UpdateAddHTLC {
+ channel_id: self.channel_id(),
+ htlc_id: htlc.htlc_id,
+ amount_msat: htlc.amount_msat,
+ payment_hash: htlc.payment_hash,
+ cltv_expiry: htlc.cltv_expiry,
+ onion_routing_packet: (**onion_packet).clone(),
+ });
+ }
+ }
+
+ for htlc in self.pending_inbound_htlcs.iter() {
+ if let &InboundHTLCState::LocalRemoved(ref reason) = &htlc.state {
+ match reason {
+ &InboundHTLCRemovalReason::FailRelay(ref err_packet) => {
+ update_fail_htlcs.push(msgs::UpdateFailHTLC {
+ channel_id: self.channel_id(),
+ htlc_id: htlc.htlc_id,
+ reason: err_packet.clone()
+ });
+ },
+ &InboundHTLCRemovalReason::FailMalformed((ref sha256_of_onion, ref failure_code)) => {
+ update_fail_malformed_htlcs.push(msgs::UpdateFailMalformedHTLC {
+ channel_id: self.channel_id(),
+ htlc_id: htlc.htlc_id,
+ sha256_of_onion: sha256_of_onion.clone(),
+ failure_code: failure_code.clone(),
+ });
+ },
+ &InboundHTLCRemovalReason::Fulfill(ref payment_preimage) => {
+ update_fulfill_htlcs.push(msgs::UpdateFulfillHTLC {
+ channel_id: self.channel_id(),
+ htlc_id: htlc.htlc_id,
+ payment_preimage: payment_preimage.clone(),
+ });
+ },
+ }
+ }
+ }
+
+ return Ok((resend_funding_locked, required_revoke,
Some(msgs::CommitmentUpdate {
- update_add_htlcs: Vec::new(),
- update_fulfill_htlcs: Vec::new(),
- update_fail_htlcs: Vec::new(),
- update_fail_malformed_htlcs: Vec::new(),
+ update_add_htlcs, update_fulfill_htlcs, update_fail_htlcs, update_fail_malformed_htlcs,
+ update_fee: None, //TODO: We need to support re-generating any update_fees in the last commitment_signed!
commitment_signed: self.send_commitment_no_state_update().expect("It looks like we failed to re-generate a commitment_signed we had previously sent?").0,
}), None));
} else {
- return Err(HandleError{err: "Peer attempted to reestablish channel with a very old remote commitment transaction", action: Some(msgs::ErrorAction::SendErrorMessage{msg: msgs::ErrorMessage{data: "Peer attempted to reestablish channel with a very old remote commitment transaction".to_string(), channel_id: msg.channel_id}})});
+ return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old remote commitment transaction"));
}
}
return Ok((None, None, Vec::new()));
}
for htlc in self.pending_inbound_htlcs.iter() {
- if htlc.state == InboundHTLCState::RemoteAnnounced {
+ if let InboundHTLCState::RemoteAnnounced(_) = htlc.state {
return Err(HandleError{err: "Got shutdown with remote pending HTLCs", action: None});
}
}
}
});
for htlc in self.pending_outbound_htlcs.iter() {
- if htlc.state == OutboundHTLCState::LocalAnnounced {
+ if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
return Ok((None, None, dropped_outbound_htlcs));
}
}
self.our_htlc_minimum_msat
}
+ /// Allowed in any state (including after shutdown)
+ pub fn get_their_htlc_minimum_msat(&self) -> u64 {
+ self.our_htlc_minimum_msat
+ }
+
pub fn get_value_satoshis(&self) -> u64 {
self.channel_value_satoshis
}
+ #[cfg(test)]
+ pub fn get_feerate(&self) -> u64 {
+ self.feerate_per_kw
+ }
+
//TODO: Testing purpose only, should be changed in another way after #81
#[cfg(test)]
pub fn get_local_keys(&self) -> &ChannelKeys {
&self.local_keys
}
+ #[cfg(test)]
+ pub fn get_value_stat(&self) -> ChannelValueStat {
+ ChannelValueStat {
+ value_to_self_msat: self.value_to_self_msat,
+ channel_value_msat: self.channel_value_satoshis * 1000,
+ channel_reserve_msat: self.their_channel_reserve_satoshis * 1000,
+ pending_outbound_htlcs_amount_msat: self.pending_outbound_htlcs.iter().map(|ref h| h.amount_msat).sum::<u64>(),
+ pending_inbound_htlcs_amount_msat: self.pending_inbound_htlcs.iter().map(|ref h| h.amount_msat).sum::<u64>(),
+ holding_cell_outbound_amount_msat: {
+ let mut res = 0;
+ for h in self.holding_cell_htlc_updates.iter() {
+ match h {
+ &HTLCUpdateAwaitingACK::AddHTLC{amount_msat, .. } => {
+ res += amount_msat;
+ }
+ _ => {}
+ }
+ }
+ res
+ },
+ their_max_htlc_value_in_flight_msat: self.their_max_htlc_value_in_flight_msat,
+ }
+ }
+
/// Allowed in any state (including after shutdown)
pub fn get_channel_update_count(&self) -> u32 {
self.channel_update_count
self.announce_publicly
}
+ pub fn is_outbound(&self) -> bool {
+ self.channel_outbound
+ }
+
/// Gets the fee we'd want to charge for adding an HTLC output to this Channel
/// Allowed in any state (including after shutdown)
pub fn get_our_fee_base_msat(&self, fee_estimator: &FeeEstimator) -> u32 {
/// apply - no calls may be made except those explicitly stated to be allowed post-shutdown.
/// Only returns an ErrorAction of DisconnectPeer, if Err.
pub fn block_connected(&mut self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]) -> Result<Option<msgs::FundingLocked>, HandleError> {
- let non_shutdown_state = self.channel_state & (!BOTH_SIDES_SHUTDOWN_MASK);
+ let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS);
if self.funding_tx_confirmations > 0 {
if header.bitcoin_hash() != self.last_block_connected {
self.last_block_connected = header.bitcoin_hash();
self.channel_state |= ChannelState::OurFundingLocked as u32;
true
} else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32) {
- self.channel_state = ChannelState::ChannelFunded as u32 | (self.channel_state & BOTH_SIDES_SHUTDOWN_MASK);
+ self.channel_state = ChannelState::ChannelFunded as u32 | (self.channel_state & MULTI_STATE_FLAGS);
self.channel_update_count += 1;
true
- } else if self.channel_state == (ChannelState::FundingSent as u32 | ChannelState::OurFundingLocked as u32) {
+ } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::OurFundingLocked as u32) {
// We got a reorg but not enough to trigger a force close, just update
// funding_tx_confirmed_in and return.
false
let funding_script = self.get_funding_redeemscript();
let remote_keys = self.build_remote_transaction_keys()?;
- let remote_initial_commitment_tx = self.build_commitment_transaction(self.cur_remote_commitment_transaction_number, &remote_keys, false, false).0;
+ let remote_initial_commitment_tx = self.build_commitment_transaction(self.cur_remote_commitment_transaction_number, &remote_keys, false, false, self.feerate_per_kw).0;
let remote_sighash = Message::from_slice(&bip143::SighashComponents::new(&remote_initial_commitment_tx).sighash_all(&remote_initial_commitment_tx.input[0], &funding_script, self.channel_value_satoshis)[..]).unwrap();
// We sign the "remote" commitment transaction, allowing them to broadcast the tx if they wish.
/// closing).
/// Note that the "channel must be funded" requirement is stricter than BOLT 7 requires - see
/// https://github.com/lightningnetwork/lightning-rfc/issues/468
- pub fn get_channel_announcement(&self, our_node_id: PublicKey, chain_hash: Sha256dHash) -> Result<(msgs::UnsignedChannelAnnouncement, Signature), HandleError> {
+ pub fn get_channel_announcement(&self, our_node_id: PublicKey, chain_hash: Sha256dHash) -> Result<(msgs::UnsignedChannelAnnouncement, Signature), ChannelError> {
if !self.announce_publicly {
- return Err(HandleError{err: "Channel is not available for public announcements", action: Some(msgs::ErrorAction::IgnoreError)});
+ return Err(ChannelError::Ignore("Channel is not available for public announcements"));
}
if self.channel_state & (ChannelState::ChannelFunded as u32) == 0 {
- return Err(HandleError{err: "Cannot get a ChannelAnnouncement until the channel funding has been locked", action: Some(msgs::ErrorAction::IgnoreError)});
+ return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement until the channel funding has been locked"));
}
if (self.channel_state & (ChannelState::LocalShutdownSent as u32 | ChannelState::ShutdownComplete as u32)) != 0 {
- return Err(HandleError{err: "Cannot get a ChannelAnnouncement once the channel is closing", action: Some(msgs::ErrorAction::IgnoreError)});
+ return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement once the channel is closing"));
}
let were_node_one = our_node_id.serialize()[..] < self.their_node_id.serialize()[..];
return Err(HandleError{err: "Cannot send an HTLC while disconnected", action: Some(ErrorAction::IgnoreError)});
}
- let (_, outbound_htlc_count, htlc_outbound_value_msat, htlc_inbound_value_msat) = self.get_pending_htlc_stats(false);
+ let (outbound_htlc_count, htlc_outbound_value_msat) = self.get_outbound_pending_htlc_stats();
if outbound_htlc_count + 1 > self.their_max_accepted_htlcs as u32 {
return Err(HandleError{err: "Cannot push more than their max accepted HTLCs", action: None});
}
if htlc_outbound_value_msat + amount_msat > self.their_max_htlc_value_in_flight_msat {
return Err(HandleError{err: "Cannot send value that would put us over our max HTLC value in flight", action: None});
}
- // Check their_channel_reserve_satoshis:
- if htlc_inbound_value_msat + htlc_outbound_value_msat + amount_msat + (self.channel_value_satoshis * 1000 - self.value_to_self_msat) > (self.channel_value_satoshis - self.their_channel_reserve_satoshis) * 1000 {
+
+ let mut holding_cell_outbound_amount_msat = 0;
+ for holding_htlc in self.holding_cell_htlc_updates.iter() {
+ match holding_htlc {
+ &HTLCUpdateAwaitingACK::AddHTLC { ref amount_msat, .. } => {
+ holding_cell_outbound_amount_msat += *amount_msat;
+ }
+ _ => {}
+ }
+ }
+
+ // Check self.their_channel_reserve_satoshis (the amount we must keep as
+ // reserve for them to have something to claim if we misbehave)
+ if self.value_to_self_msat < self.their_channel_reserve_satoshis * 1000 + amount_msat + holding_cell_outbound_amount_msat + htlc_outbound_value_msat {
return Err(HandleError{err: "Cannot send value that would put us over our reserve value", action: None});
}
amount_msat: amount_msat,
payment_hash: payment_hash.clone(),
cltv_expiry: cltv_expiry,
- state: OutboundHTLCState::LocalAnnounced,
+ state: OutboundHTLCState::LocalAnnounced(Box::new(onion_routing_packet.clone())),
source,
fail_reason: None,
});
if (self.channel_state & (ChannelState::PeerDisconnected as u32)) == (ChannelState::PeerDisconnected as u32) {
panic!("Cannot create commitment tx while disconnected, as send_htlc will have returned an Err so a send_commitment precondition has been violated");
}
- let mut have_updates = false; // TODO initialize with "have we sent a fee update?"
+ let mut have_updates = self.pending_update_fee.is_some();
for htlc in self.pending_outbound_htlcs.iter() {
- if htlc.state == OutboundHTLCState::LocalAnnounced {
+ if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
have_updates = true;
}
if have_updates { break; }
// fail to generate this, we still are at least at a position where upgrading their status
// is acceptable.
for htlc in self.pending_inbound_htlcs.iter_mut() {
- if htlc.state == InboundHTLCState::AwaitingRemoteRevokeToAnnounce {
- htlc.state = InboundHTLCState::AwaitingAnnouncedRemoteRevoke;
+ let new_state = if let &InboundHTLCState::AwaitingRemoteRevokeToAnnounce(ref forward_info) = &htlc.state {
+ Some(InboundHTLCState::AwaitingAnnouncedRemoteRevoke(forward_info.clone()))
+ } else { None };
+ if let Some(state) = new_state {
+ htlc.state = state;
}
}
for htlc in self.pending_outbound_htlcs.iter_mut() {
- if htlc.state == OutboundHTLCState::AwaitingRemoteRevokeToRemove {
+ if let OutboundHTLCState::AwaitingRemoteRevokeToRemove = htlc.state {
htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke;
}
}
fn send_commitment_no_state_update(&self) -> Result<(msgs::CommitmentSigned, (Transaction, Vec<HTLCOutputInCommitment>)), HandleError> {
let funding_script = self.get_funding_redeemscript();
+ let mut feerate_per_kw = self.feerate_per_kw;
+ if let Some(feerate) = self.pending_update_fee {
+ if self.channel_outbound {
+ feerate_per_kw = feerate;
+ }
+ }
+
let remote_keys = self.build_remote_transaction_keys()?;
- let remote_commitment_tx = self.build_commitment_transaction(self.cur_remote_commitment_transaction_number, &remote_keys, false, true);
+ let remote_commitment_tx = self.build_commitment_transaction(self.cur_remote_commitment_transaction_number, &remote_keys, false, true, feerate_per_kw);
let remote_commitment_txid = remote_commitment_tx.0.txid();
let remote_sighash = Message::from_slice(&bip143::SighashComponents::new(&remote_commitment_tx.0).sighash_all(&remote_commitment_tx.0.input[0], &funding_script, self.channel_value_satoshis)[..]).unwrap();
let our_sig = self.secp_ctx.sign(&remote_sighash, &self.local_keys.funding_key);
let mut htlc_sigs = Vec::new();
for ref htlc in remote_commitment_tx.1.iter() {
- let htlc_tx = self.build_htlc_transaction(&remote_commitment_txid, htlc, false, &remote_keys);
+ 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 our_htlc_key = secp_derived_key!(chan_utils::derive_private_key(&self.secp_ctx, &remote_keys.per_commitment_point, &self.local_keys.htlc_base_key), self.channel_id());
/// Begins the shutdown process, getting a message for the remote peer and returning all
/// holding cell HTLCs for payment failure.
- pub fn get_shutdown(&mut self) -> Result<(msgs::Shutdown, Vec<(HTLCSource, [u8; 32])>), HandleError> {
+ pub fn get_shutdown(&mut self) -> Result<(msgs::Shutdown, Vec<(HTLCSource, [u8; 32])>), APIError> {
for htlc in self.pending_outbound_htlcs.iter() {
- if htlc.state == OutboundHTLCState::LocalAnnounced {
- return Err(HandleError{err: "Cannot begin shutdown with pending HTLCs, call send_commitment first", action: None});
+ if let OutboundHTLCState::LocalAnnounced(_) = htlc.state {
+ return Err(APIError::APIMisuseError{err: "Cannot begin shutdown with pending HTLCs. Process pending events first"});
}
}
if self.channel_state & BOTH_SIDES_SHUTDOWN_MASK != 0 {
- return Err(HandleError{err: "Shutdown already in progress", action: None});
+ if (self.channel_state & ChannelState::LocalShutdownSent as u32) == ChannelState::LocalShutdownSent as u32 {
+ return Err(APIError::APIMisuseError{err: "Shutdown already in progress"});
+ }
+ else if (self.channel_state & ChannelState::RemoteShutdownSent as u32) == ChannelState::RemoteShutdownSent as u32 {
+ return Err(APIError::ChannelUnavailable{err: "Shutdown already in progress by remote"});
+ }
}
assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0);
if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
- return Err(HandleError{err: "Cannot begin shutdown while peer is disconnected, maybe force-close instead?", action: None});
+ return Err(APIError::ChannelUnavailable{err: "Cannot begin shutdown while peer is disconnected, maybe force-close instead?"});
}
let our_closing_script = self.get_closing_scriptpubkey();
macro_rules! test_commitment {
( $their_sig_hex: expr, $our_sig_hex: expr, $tx_hex: expr) => {
- unsigned_tx = chan.build_commitment_transaction(0xffffffffffff - 42, &keys, true, false);
+ unsigned_tx = chan.build_commitment_transaction(0xffffffffffff - 42, &keys, true, false, chan.feerate_per_kw);
let their_signature = Signature::from_der(&secp_ctx, &hex::decode($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()).unwrap();
let remote_signature = Signature::from_der(&secp_ctx, &hex::decode($their_sig_hex).unwrap()[..]).unwrap();
let ref htlc = unsigned_tx.1[$htlc_idx];
- let mut htlc_tx = chan.build_htlc_transaction(&unsigned_tx.0.txid(), &htlc, true, &keys);
+ 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();
secp_ctx.verify(&htlc_sighash, &remote_signature, &keys.b_htlc_key).unwrap();
cltv_expiry: 500,
payment_hash: [0; 32],
state: InboundHTLCState::Committed,
- local_removed_fulfilled: false,
- pending_forward_state: None,
};
let mut sha = Sha256::new();
sha.input(&hex::decode("0000000000000000000000000000000000000000000000000000000000000000").unwrap());
cltv_expiry: 501,
payment_hash: [0; 32],
state: InboundHTLCState::Committed,
- local_removed_fulfilled: false,
- pending_forward_state: None,
};
let mut sha = Sha256::new();
sha.input(&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap());
cltv_expiry: 504,
payment_hash: [0; 32],
state: InboundHTLCState::Committed,
- local_removed_fulfilled: false,
- pending_forward_state: None,
};
let mut sha = Sha256::new();
sha.input(&hex::decode("0404040404040404040404040404040404040404040404040404040404040404").unwrap());