X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fln%2Fchannel.rs;h=6a4c8613ba6f3557c63ee6ad354678c9e6092bb2;hb=c86657ea6c751f6ce349ae3315a6c8ab0e0f5850;hp=f4e40d6523db9aa379d407bb6c2dddceeed3d2aa;hpb=f0bcb7dba0360f04afacfff63ec2598d39bddc5f;p=rust-lightning diff --git a/src/ln/channel.rs b/src/ln/channel.rs index f4e40d65..6a4c8613 100644 --- a/src/ln/channel.rs +++ b/src/ln/channel.rs @@ -2,13 +2,14 @@ use bitcoin::blockdata::block::BlockHeader; use bitcoin::blockdata::script::{Script,Builder}; use bitcoin::blockdata::transaction::{TxIn, TxOut, Transaction, SigHashType}; use bitcoin::blockdata::opcodes; -use bitcoin::util::hash::{BitcoinHash, Sha256dHash}; +use bitcoin::util::hash::BitcoinHash; use bitcoin::util::bip143; use bitcoin::consensus::encode::{self, Encodable, Decodable}; use bitcoin_hashes::{Hash, HashEngine}; use bitcoin_hashes::sha256::Hash as Sha256; use bitcoin_hashes::hash160::Hash as Hash160; +use bitcoin_hashes::sha256d::Hash as Sha256dHash; use secp256k1::key::{PublicKey,SecretKey}; use secp256k1::{Secp256k1,Signature}; @@ -106,19 +107,19 @@ enum OutboundHTLCState { 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). - RemoteRemoved, + RemoteRemoved(Option), /// Remote removed this and sent a commitment_signed (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 /// can do any backwards failing. Implies AwaitingRemoteRevoke. /// We also have not yet removed 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. - AwaitingRemoteRevokeToRemove, + AwaitingRemoteRevokeToRemove(Option), /// Remote removed this and sent a commitment_signed (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 /// can do any backwards failing. Implies AwaitingRemoteRevoke. /// We have removed this HTLC in our latest commitment_signed and are now just waiting on a /// revoke_and_ack to drop completely. - AwaitingRemovedRemoteRevoke, + AwaitingRemovedRemoteRevoke(Option), } struct OutboundHTLCOutput { @@ -128,8 +129,6 @@ struct OutboundHTLCOutput { payment_hash: PaymentHash, state: OutboundHTLCState, source: HTLCSource, - /// If we're in a removed state, set if they failed, otherwise None - fail_reason: Option, } /// See AwaitingRemoteRevoke ChannelState for more info @@ -858,9 +857,9 @@ impl Channel { let (include, state_name) = match htlc.state { OutboundHTLCState::LocalAnnounced(_) => (generated_by_local, "LocalAnnounced"), OutboundHTLCState::Committed => (true, "Committed"), - OutboundHTLCState::RemoteRemoved => (generated_by_local, "RemoteRemoved"), - OutboundHTLCState::AwaitingRemoteRevokeToRemove => (generated_by_local, "AwaitingRemoteRevokeToRemove"), - OutboundHTLCState::AwaitingRemovedRemoteRevoke => (false, "AwaitingRemovedRemoteRevoke"), + OutboundHTLCState::RemoteRemoved(_) => (generated_by_local, "RemoteRemoved"), + OutboundHTLCState::AwaitingRemoteRevokeToRemove(_) => (generated_by_local, "AwaitingRemoteRevokeToRemove"), + OutboundHTLCState::AwaitingRemovedRemoteRevoke(_) => (false, "AwaitingRemovedRemoteRevoke"), }; if include { @@ -869,13 +868,11 @@ impl Channel { } else { log_trace!(self, " ...not including outbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, log_bytes!(htlc.payment_hash.0), htlc.amount_msat, state_name); match htlc.state { - OutboundHTLCState::AwaitingRemoteRevokeToRemove|OutboundHTLCState::AwaitingRemovedRemoteRevoke => { - if htlc.fail_reason.is_none() { - value_to_self_msat_offset -= htlc.amount_msat as i64; - } + OutboundHTLCState::AwaitingRemoteRevokeToRemove(None)|OutboundHTLCState::AwaitingRemovedRemoteRevoke(None) => { + value_to_self_msat_offset -= htlc.amount_msat as i64; }, - OutboundHTLCState::RemoteRemoved => { - if !generated_by_local && htlc.fail_reason.is_none() { + OutboundHTLCState::RemoteRemoved(None) => { + if !generated_by_local { value_to_self_msat_offset -= htlc.amount_msat as i64; } }, @@ -1225,6 +1222,7 @@ impl Channel { _ => {} } } + log_trace!(self, "Adding HTLC claim to holding_cell! Current state: {}", self.channel_state); self.holding_cell_htlc_updates.push(HTLCUpdateAwaitingACK::ClaimHTLC { payment_preimage: payment_preimage_arg, htlc_id: htlc_id_arg, }); @@ -1238,6 +1236,7 @@ impl Channel { 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()))); } + log_trace!(self, "Upgrading HTLC {} to LocalRemoved with a Fulfill!", log_bytes!(htlc.payment_hash.0)); htlc.state = InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(payment_preimage_arg.clone())); } @@ -1596,7 +1595,24 @@ impl Channel { // 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 + msg.amount_msat + self.value_to_self_msat > (self.channel_value_satoshis - Channel::get_our_channel_reserve_satoshis(self.channel_value_satoshis)) * 1000 { + // Note that we don't really care about having a small/no to_remote output in our local + // commitment transactions, as the purpose of the channel reserve is to ensure we can + // punish *them* if they misbehave, so we discount any outbound HTLCs which will not be + // present in the next commitment transaction we send them (at least for fulfilled ones, + // failed ones won't modify value_to_self). + // Note that we will send HTLCs which another instance of rust-lightning would think + // violate the reserve value if we do not do this (as we forget inbound HTLCs from the + // Channel state once they will not be present in the next received commitment + // transaction). + let mut removed_outbound_total_msat = 0; + for ref htlc in self.pending_outbound_htlcs.iter() { + if let OutboundHTLCState::AwaitingRemoteRevokeToRemove(None) = htlc.state { + removed_outbound_total_msat += htlc.amount_msat; + } else if let OutboundHTLCState::AwaitingRemovedRemoteRevoke(None) = htlc.state { + removed_outbound_total_msat += htlc.amount_msat; + } + } + 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 + removed_outbound_total_msat { return Err(ChannelError::Close("Remote HTLC add would put them over their reserve value")); } if self.next_remote_htlc_id != msg.htlc_id { @@ -1642,10 +1658,9 @@ impl Channel { OutboundHTLCState::LocalAnnounced(_) => return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC before it had been committed")), OutboundHTLCState::Committed => { - htlc.state = OutboundHTLCState::RemoteRemoved; - htlc.fail_reason = fail_reason; + htlc.state = OutboundHTLCState::RemoteRemoved(fail_reason); }, - OutboundHTLCState::AwaitingRemoteRevokeToRemove | OutboundHTLCState::AwaitingRemovedRemoteRevoke | OutboundHTLCState::RemoteRemoved => + OutboundHTLCState::AwaitingRemoteRevokeToRemove(_) | OutboundHTLCState::AwaitingRemovedRemoteRevoke(_) | OutboundHTLCState::RemoteRemoved(_) => return Err(ChannelError::Close("Remote tried to fulfill/fail HTLC that they'd already fulfilled/failed")), } return Ok(&htlc.source); @@ -1798,8 +1813,10 @@ impl Channel { } } for htlc in self.pending_outbound_htlcs.iter_mut() { - if let OutboundHTLCState::RemoteRemoved = htlc.state { - htlc.state = OutboundHTLCState::AwaitingRemoteRevokeToRemove; + if let Some(fail_reason) = if let &mut OutboundHTLCState::RemoteRemoved(ref mut fail_reason) = &mut htlc.state { + Some(fail_reason.take()) + } else { None } { + htlc.state = OutboundHTLCState::AwaitingRemoteRevokeToRemove(fail_reason); need_our_commitment = true; } } @@ -1852,6 +1869,8 @@ impl Channel { fn free_holding_cell_htlcs(&mut self) -> Result, ChannelError> { assert_eq!(self.channel_state & ChannelState::MonitorUpdateFailed as u32, 0); if self.holding_cell_htlc_updates.len() != 0 || self.holding_cell_update_fee.is_some() { + log_trace!(self, "Freeing holding cell with {} HTLC updates{}", self.holding_cell_htlc_updates.len(), if self.holding_cell_update_fee.is_some() { " and a fee update" } else { "" }); + 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()); @@ -2017,9 +2036,9 @@ impl Channel { } else { true } }); pending_outbound_htlcs.retain(|htlc| { - if let OutboundHTLCState::AwaitingRemovedRemoteRevoke = htlc.state { + if let &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref fail_reason) = &htlc.state { log_trace!(logger, " ...removing outbound AwaitingRemovedRemoteRevoke {}", log_bytes!(htlc.payment_hash.0)); - if let Some(reason) = htlc.fail_reason.clone() { // We really want take() here, but, again, non-mut ref :( + if let Some(reason) = fail_reason.clone() { // We really want take() here, but, again, non-mut ref :( revoked_htlcs.push((htlc.source.clone(), htlc.payment_hash, reason)); } else { // They fulfilled, so we sent them money @@ -2070,9 +2089,12 @@ impl Channel { if let OutboundHTLCState::LocalAnnounced(_) = htlc.state { log_trace!(logger, " ...promoting outbound LocalAnnounced {} to Committed", log_bytes!(htlc.payment_hash.0)); htlc.state = OutboundHTLCState::Committed; - } else if let OutboundHTLCState::AwaitingRemoteRevokeToRemove = htlc.state { + } + if let Some(fail_reason) = if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut fail_reason) = &mut htlc.state { + Some(fail_reason.take()) + } else { None } { log_trace!(logger, " ...promoting outbound AwaitingRemoteRevokeToRemove {} to AwaitingRemovedRemoteRevoke", log_bytes!(htlc.payment_hash.0)); - htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke; + htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(fail_reason); require_commitment = true; } } @@ -2228,7 +2250,7 @@ impl Channel { self.next_remote_htlc_id -= inbound_drop_count; for htlc in self.pending_outbound_htlcs.iter_mut() { - if let OutboundHTLCState::RemoteRemoved = htlc.state { + 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. @@ -2371,6 +2393,8 @@ impl Channel { } } + log_trace!(self, "Regenerated latest commitment update with {} update_adds, {} update_fulfills, {} update_fails, and {} update_fail_malformeds", + update_add_htlcs.len(), update_fulfill_htlcs.len(), update_fail_htlcs.len(), update_fail_malformed_htlcs.len()); msgs::CommitmentUpdate { 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! @@ -3139,7 +3163,7 @@ impl Channel { excess_data: Vec::new(), }; - let msghash = hash_to_message!(&Sha256dHash::from_data(&msg.encode()[..])[..]); + let msghash = hash_to_message!(&Sha256dHash::hash(&msg.encode()[..])[..]); let sig = self.secp_ctx.sign(&msghash, &self.local_keys.funding_key); Ok((msg, sig)) @@ -3244,7 +3268,6 @@ impl Channel { cltv_expiry: cltv_expiry, state: OutboundHTLCState::LocalAnnounced(Box::new(onion_routing_packet.clone())), source, - fail_reason: None, }); let res = msgs::UpdateAddHTLC { @@ -3309,8 +3332,10 @@ impl Channel { } } for htlc in self.pending_outbound_htlcs.iter_mut() { - if let OutboundHTLCState::AwaitingRemoteRevokeToRemove = htlc.state { - htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke; + if let Some(fail_reason) = if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut fail_reason) = &mut htlc.state { + Some(fail_reason.take()) + } else { None } { + htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(fail_reason); } } @@ -3576,7 +3601,6 @@ impl Writeable for Channel { htlc.cltv_expiry.write(writer)?; htlc.payment_hash.write(writer)?; htlc.source.write(writer)?; - write_option!(htlc.fail_reason); match &htlc.state { &OutboundHTLCState::LocalAnnounced(ref onion_packet) => { 0u8.write(writer)?; @@ -3585,14 +3609,17 @@ impl Writeable for Channel { &OutboundHTLCState::Committed => { 1u8.write(writer)?; }, - &OutboundHTLCState::RemoteRemoved => { + &OutboundHTLCState::RemoteRemoved(ref fail_reason) => { 2u8.write(writer)?; + write_option!(*fail_reason); }, - &OutboundHTLCState::AwaitingRemoteRevokeToRemove => { + &OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref fail_reason) => { 3u8.write(writer)?; + write_option!(*fail_reason); }, - &OutboundHTLCState::AwaitingRemovedRemoteRevoke => { + &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref fail_reason) => { 4u8.write(writer)?; + write_option!(*fail_reason); }, } } @@ -3755,13 +3782,12 @@ impl ReadableArgs> for Channel { cltv_expiry: Readable::read(reader)?, payment_hash: Readable::read(reader)?, source: Readable::read(reader)?, - fail_reason: Readable::read(reader)?, state: match >::read(reader)? { 0 => OutboundHTLCState::LocalAnnounced(Box::new(Readable::read(reader)?)), 1 => OutboundHTLCState::Committed, - 2 => OutboundHTLCState::RemoteRemoved, - 3 => OutboundHTLCState::AwaitingRemoteRevokeToRemove, - 4 => OutboundHTLCState::AwaitingRemovedRemoteRevoke, + 2 => OutboundHTLCState::RemoteRemoved(Readable::read(reader)?), + 3 => OutboundHTLCState::AwaitingRemoteRevokeToRemove(Readable::read(reader)?), + 4 => OutboundHTLCState::AwaitingRemovedRemoteRevoke(Readable::read(reader)?), _ => return Err(DecodeError::InvalidValue), }, }); @@ -3950,12 +3976,12 @@ impl ReadableArgs> for Channel { #[cfg(test)] mod tests { - use bitcoin::util::hash::{Sha256dHash, Hash160}; use bitcoin::util::bip143; use bitcoin::consensus::encode::serialize; use bitcoin::blockdata::script::{Script, Builder}; use bitcoin::blockdata::transaction::Transaction; use bitcoin::blockdata::opcodes; + use bitcoin_hashes::hex::FromHex; use hex; use ln::channelmanager::{HTLCSource, PaymentPreimage, PaymentHash}; use ln::channel::{Channel,ChannelKeys,InboundHTLCOutput,OutboundHTLCOutput,InboundHTLCState,OutboundHTLCState,HTLCOutputInCommitment,TxCreationKeys}; @@ -3970,6 +3996,8 @@ mod tests { use secp256k1::{Secp256k1,Message,Signature}; use secp256k1::key::{SecretKey,PublicKey}; use bitcoin_hashes::sha256::Hash as Sha256; + use bitcoin_hashes::sha256d::Hash as Sha256dHash; + use bitcoin_hashes::hash160::Hash as Hash160; use bitcoin_hashes::Hash; use std::sync::Arc; @@ -3996,7 +4024,7 @@ mod tests { fn get_destination_script(&self) -> Script { let secp_ctx = Secp256k1::signing_only(); let channel_monitor_claim_key = SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(); - let our_channel_monitor_claim_key_hash = Hash160::from_data(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize()); + let our_channel_monitor_claim_key_hash = Hash160::hash(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize()); Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_monitor_claim_key_hash[..]).into_script() } @@ -4154,7 +4182,6 @@ mod tests { payment_hash: PaymentHash([0; 32]), state: OutboundHTLCState::Committed, source: HTLCSource::dummy(), - fail_reason: None, }; out.payment_hash.0 = Sha256::hash(&hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap()).into_inner(); out @@ -4167,7 +4194,6 @@ mod tests { payment_hash: PaymentHash([0; 32]), state: OutboundHTLCState::Committed, source: HTLCSource::dummy(), - fail_reason: None, }; out.payment_hash.0 = Sha256::hash(&hex::decode("0303030303030303030303030303030303030303030303030303030303030303").unwrap()).into_inner(); out