Logging interface
[rust-lightning] / src / ln / channel.rs
index fc67085250a3e1f429d53dcffa06f7ff3455a0d9..a1ca7e25e65a5ea8cff9f5d0a27710751ea5ae94 100644 (file)
@@ -14,7 +14,7 @@ use crypto::digest::Digest;
 use crypto::hkdf::{hkdf_extract,hkdf_expand};
 
 use ln::msgs;
-use ln::msgs::{HandleError, MsgEncodable};
+use ln::msgs::{ErrorAction, HandleError, MsgEncodable};
 use ln::channelmonitor::ChannelMonitor;
 use ln::channelmanager::{PendingForwardHTLCInfo, HTLCFailReason};
 use ln::chan_utils::{TxCreationKeys,HTLCOutputInCommitment,HTLC_SUCCESS_TX_WEIGHT,HTLC_TIMEOUT_TX_WEIGHT};
@@ -23,11 +23,13 @@ use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
 use chain::transaction::OutPoint;
 use util::{transaction_utils,rng};
 use util::sha2::Sha256;
+use util::logger::{Logger, Record};
 
 use std;
 use std::default::Default;
 use std::{cmp,mem};
 use std::time::Instant;
+use std::sync::{Arc};
 
 pub struct ChannelKeys {
        pub funding_key: SecretKey,
@@ -136,7 +138,7 @@ enum HTLCState {
        /// anyway).
        LocalRemoved,
        /// Removed by us, sent a new commitment_signed and got a revoke_and_ack. Just waiting on an
-       /// updated local commitment transaction.
+       /// updated local commitment transaction. Implies local_removed_fulfilled.
        LocalRemovedAwaitingCommitment,
 }
 
@@ -271,7 +273,7 @@ pub struct Channel {
        /// to detect unconfirmation after a serialize-unserialize roudtrip where we may not see a full
        /// series of block_connected/block_disconnected calls. Obviously this is not a guarantee as we
        /// could miss the funding_tx_confirmed_in block as well, but it serves as a useful fallback.
-       funding_tx_confirmed_in: Sha256dHash,
+       funding_tx_confirmed_in: Option<Sha256dHash>,
        short_channel_id: Option<u64>,
        /// Used to deduplicate block_connected callbacks
        last_block_connected: Sha256dHash,
@@ -303,6 +305,8 @@ pub struct Channel {
        their_shutdown_scriptpubkey: Option<Script>,
 
        channel_monitor: ChannelMonitor,
+
+       logger: Arc<Logger>,
 }
 
 const OUR_MAX_HTLCS: u16 = 5; //TODO
@@ -361,7 +365,7 @@ impl Channel {
        // Constructors:
 
        /// panics if channel_value_satoshis is >= `MAX_FUNDING_SATOSHIS`
-       pub fn new_outbound(fee_estimator: &FeeEstimator, chan_keys: ChannelKeys, their_node_id: PublicKey, channel_value_satoshis: u64, announce_publicly: bool, user_id: u64) -> Channel {
+       pub fn new_outbound(fee_estimator: &FeeEstimator, chan_keys: ChannelKeys, their_node_id: PublicKey, channel_value_satoshis: u64, announce_publicly: bool, user_id: u64, logger: Arc<Logger>) -> Channel {
                if channel_value_satoshis >= MAX_FUNDING_SATOSHIS {
                        panic!("funding value > 2^24");
                }
@@ -401,7 +405,7 @@ impl Channel {
 
                        last_sent_closing_fee: None,
 
-                       funding_tx_confirmed_in: Default::default(),
+                       funding_tx_confirmed_in: None,
                        short_channel_id: None,
                        last_block_connected: Default::default(),
                        funding_tx_confirmations: 0,
@@ -429,6 +433,8 @@ impl Channel {
                        their_shutdown_scriptpubkey: None,
 
                        channel_monitor: channel_monitor,
+
+                       logger,
                }
        }
 
@@ -446,7 +452,7 @@ impl Channel {
        /// 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) -> Result<Channel, HandleError> {
+       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> {
                // Check sanity of message fields:
                if msg.funding_satoshis >= MAX_FUNDING_SATOSHIS {
                        return Err(HandleError{err: "funding value > 2^24", action: Some(msgs::ErrorAction::DisconnectPeer{ msg: None })});
@@ -519,7 +525,7 @@ impl Channel {
 
                        last_sent_closing_fee: None,
 
-                       funding_tx_confirmed_in: Default::default(),
+                       funding_tx_confirmed_in: None,
                        short_channel_id: None,
                        last_block_connected: Default::default(),
                        funding_tx_confirmations: 0,
@@ -548,6 +554,8 @@ impl Channel {
                        their_shutdown_scriptpubkey: None,
 
                        channel_monitor: channel_monitor,
+
+                       logger,
                };
 
                let obscure_factor = chan.get_commitment_transaction_number_obscure_factor();
@@ -663,7 +671,7 @@ impl Channel {
                        } else {
                                match htlc.state {
                                        HTLCState::AwaitingRemoteRevokeToRemove|HTLCState::AwaitingRemovedRemoteRevoke => {
-                                               if generated_by_local && htlc.fail_reason.is_none() {
+                                               if htlc.fail_reason.is_none() {
                                                        value_to_self_msat_offset -= htlc.amount_msat as i64;
                                                }
                                        },
@@ -678,6 +686,7 @@ impl Channel {
                                                }
                                        },
                                        HTLCState::LocalRemovedAwaitingCommitment => {
+                                               assert!(htlc.local_removed_fulfilled);
                                                value_to_self_msat_offset += htlc.amount_msat as i64;
                                        },
                                        _ => {},
@@ -985,7 +994,7 @@ impl Channel {
                }
 
                let htlc_id = {
-                       let mut htlc = &mut self.pending_htlcs[pending_idx];
+                       let htlc = &mut self.pending_htlcs[pending_idx];
                        if htlc.state == HTLCState::Committed {
                                htlc.state = HTLCState::LocalRemoved;
                                htlc.local_removed_fulfilled = true;
@@ -1356,7 +1365,7 @@ impl Channel {
                Err(HandleError{err: "Remote tried to fulfill/fail an HTLC we couldn't find", action: None})
        }
 
-       pub fn update_fulfill_htlc(&mut self, msg: &msgs::UpdateFulfillHTLC) -> Result<ChannelMonitor, HandleError> {
+       pub fn update_fulfill_htlc(&mut self, msg: &msgs::UpdateFulfillHTLC) -> Result<(), HandleError> {
                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});
                }
@@ -1366,9 +1375,8 @@ impl Channel {
                let mut payment_hash = [0; 32];
                sha.result(&mut payment_hash);
 
-               self.channel_monitor.provide_payment_preimage(&payment_hash, &msg.payment_preimage);
                self.mark_outbound_htlc_removed(msg.htlc_id, Some(payment_hash), None)?;
-               Ok(self.channel_monitor.clone())
+               Ok(())
        }
 
        pub fn update_fail_htlc(&mut self, msg: &msgs::UpdateFailHTLC, fail_reason: HTLCFailReason) -> Result<[u8; 32], HandleError> {
@@ -1748,7 +1756,7 @@ impl Channel {
 
                match self.secp_ctx.verify(&sighash, &msg.signature, &self.their_funding_pubkey) {
                        Ok(_) => {},
-                       Err(_) => {
+                       Err(_e) => {
                                // The remote end may have decided to revoke their output due to inconsistent dust
                                // limits, so check for that case by re-checking the signature here.
                                closing_tx = self.build_closing_transaction(msg.fee_satoshis, true).0;
@@ -1827,6 +1835,7 @@ impl Channel {
                self.user_id
        }
 
+       /// May only be called after funding has been initiated (ie is_funding_initiated() is true)
        pub fn channel_monitor(&self) -> ChannelMonitor {
                if self.channel_state < ChannelState::FundingCreated as u32 {
                        panic!("Can't get a channel monitor until funding has been created");
@@ -1904,6 +1913,11 @@ impl Channel {
                self.is_usable()
        }
 
+       /// Returns true if funding_created was sent/received.
+       pub fn is_funding_initiated(&self) -> bool {
+               self.channel_state >= ChannelState::FundingCreated as u32
+       }
+
        /// Returns true if this channel is fully shut down. True here implies that no further actions
        /// may/will be taken on this channel, and thus this object should be freed. Any future changes
        /// will be handled appropriately by the chain monitor.
@@ -1917,34 +1931,48 @@ impl Channel {
        /// Called by channelmanager based on chain blocks being connected.
        /// Note that we only need to use this to detect funding_signed, anything else is handled by
        /// the channel_monitor.
-       pub fn block_connected(&mut self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]) -> Option<msgs::FundingLocked> {
+       /// In case of Err, the channel may have been closed, at which point the standard requirements
+       /// 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);
                if self.funding_tx_confirmations > 0 {
                        if header.bitcoin_hash() != self.last_block_connected {
                                self.last_block_connected = header.bitcoin_hash();
                                self.funding_tx_confirmations += 1;
                                if self.funding_tx_confirmations == CONF_TARGET as u64 {
-                                       if non_shutdown_state == ChannelState::FundingSent as u32 {
+                                       let need_commitment_update = if non_shutdown_state == ChannelState::FundingSent as u32 {
                                                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_update_count += 1;
-                                               //TODO: Something about a state where we "lost confirmation"
+                                               true
+                                       } else if self.channel_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
                                        } else if self.channel_state < ChannelState::ChannelFunded as u32 {
-                                               panic!("Started confirming a channel in a state pre-FundingSent?");
-                                       }
-                                       self.funding_tx_confirmed_in = header.bitcoin_hash();
+                                               panic!("Started confirming a channel in a state pre-FundingSent?: {}", self.channel_state);
+                                       } else {
+                                               // We got a reorg but not enough to trigger a force close, just update
+                                               // funding_tx_confirmed_in and return.
+                                               false
+                                       };
+                                       self.funding_tx_confirmed_in = Some(header.bitcoin_hash());
 
                                        //TODO: Note that this must be a duplicate of the previous commitment point they sent us,
                                        //as otherwise we will have a commitment transaction that they can't revoke (well, kinda,
                                        //they can by sending two revoke_and_acks back-to-back, but not really). This appears to be
                                        //a protocol oversight, but I assume I'm just missing something.
-                                       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).unwrap();
-                                       return Some(msgs::FundingLocked {
-                                               channel_id: self.channel_id,
-                                               next_per_commitment_point: next_per_commitment_point,
-                                       });
+                                       if need_commitment_update {
+                                               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).unwrap();
+                                               return Ok(Some(msgs::FundingLocked {
+                                                       channel_id: self.channel_id,
+                                                       next_per_commitment_point: next_per_commitment_point,
+                                               }));
+                                       }
                                }
                        }
                }
@@ -1956,6 +1984,7 @@ impl Channel {
                                                tx.output[txo_idx].value != self.channel_value_satoshis {
                                                self.channel_state = ChannelState::ShutdownComplete as u32;
                                                self.channel_update_count += 1;
+                                               return Err(HandleError{err: "funding tx had wrong script/value", action: Some(ErrorAction::DisconnectPeer{msg: None})});
                                        } else {
                                                self.funding_tx_confirmations = 1;
                                                self.short_channel_id = Some(((height as u64)          << (5*8)) |
@@ -1965,7 +1994,7 @@ impl Channel {
                                }
                        }
                }
-               None
+               Ok(None)
        }
 
        /// Called by channelmanager based on chain blocks being disconnected.
@@ -1978,7 +2007,7 @@ impl Channel {
                                return true;
                        }
                }
-               if header.bitcoin_hash() == self.funding_tx_confirmed_in {
+               if Some(header.bitcoin_hash()) == self.funding_tx_confirmed_in {
                        self.funding_tx_confirmations = CONF_TARGET as u64 - 1;
                }
                false
@@ -2090,6 +2119,7 @@ impl Channel {
                let (our_signature, commitment_tx) = match self.get_outbound_funding_created_signature() {
                        Ok(res) => res,
                        Err(e) => {
+                               log_error!(self, "Got bad signatures: {}!", e.err);
                                self.channel_monitor.unset_funding_info();
                                return Err(e);
                        }
@@ -2388,10 +2418,13 @@ mod tests {
        use ln::chan_utils;
        use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
        use chain::transaction::OutPoint;
+       use util::test_utils;
+       use util::logger::Logger;
        use secp256k1::{Secp256k1,Message,Signature};
        use secp256k1::key::{SecretKey,PublicKey};
        use crypto::sha2::Sha256;
        use crypto::digest::Digest;
+       use std::sync::Arc;
 
        struct TestFeeEstimator {
                fee_est: u64
@@ -2412,6 +2445,7 @@ mod tests {
        fn outbound_commitment_test() {
                // Test vectors from BOLT 3 Appendix C:
                let feeest = TestFeeEstimator{fee_est: 15000};
+               let logger : Arc<Logger> = Arc::new(test_utils::TestLogger::new());
                let secp_ctx = Secp256k1::new();
 
                let chan_keys = ChannelKeys {
@@ -2429,7 +2463,7 @@ mod tests {
                assert_eq!(PublicKey::from_secret_key(&secp_ctx, &chan_keys.funding_key).unwrap().serialize()[..],
                                hex::decode("023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb").unwrap()[..]);
 
-               let mut chan = Channel::new_outbound(&feeest, chan_keys, PublicKey::new(), 10000000, false, 42); // Nothing uses their network key in this test
+               let mut chan = Channel::new_outbound(&feeest, chan_keys, PublicKey::new(), 10000000, false, 42, Arc::clone(&logger)); // Nothing uses their network key in this test
                chan.their_to_self_delay = 144;
                chan.our_dust_limit_satoshis = 546;