Implement get_chain_utxo and ChainError in
authorAntoine Riard <ariard@student.42.fr>
Thu, 30 Aug 2018 01:40:18 +0000 (01:40 +0000)
committerMatt Corallo <git@bluematt.me>
Mon, 3 Sep 2018 21:40:12 +0000 (17:40 -0400)
ChainWatchInterface to Router check on channel_announcement

Needed for BOLT 7

src/chain/chaininterface.rs
src/ln/router.rs

index bda99b2d83c2d47ecf95a274b9cdc412f47db705..78a40099c35d69687962d33c88b058c06399e6ba 100644 (file)
@@ -1,11 +1,24 @@
 use bitcoin::blockdata::block::{Block, BlockHeader};
 use bitcoin::blockdata::transaction::Transaction;
 use bitcoin::blockdata::script::Script;
+use bitcoin::blockdata::constants::genesis_block;
 use bitcoin::util::hash::Sha256dHash;
+use bitcoin::network::constants::Network;
+use bitcoin::network::serialize::BitcoinHash;
 use util::logger::Logger;
 use std::sync::{Mutex,Weak,MutexGuard,Arc};
 use std::sync::atomic::{AtomicUsize, Ordering};
 
+/// Used to give chain error details upstream
+pub enum ChainError {
+       /// Client doesn't support UTXO lookup (but the chain hash matches our genesis block hash)
+       NotSupported,
+       /// Chain isn't the one watched
+       NotWatched,
+       /// Tx doesn't exist or is unconfirmed
+       UnknownTx,
+}
+
 /// An interface to request notification of certain scripts as they appear the
 /// chain.
 /// Note that all of the functions implemented here *must* be reentrant-safe (obviously - they're
@@ -24,6 +37,12 @@ pub trait ChainWatchInterface: Sync + Send {
 
        fn register_listener(&self, listener: Weak<ChainListener>);
        //TODO: unregister
+
+       /// Gets the script and value in satoshis for a given unspent transaction output given a
+       /// short_channel_id (aka unspent_tx_output_identier). For BTC/tBTC channels the top three
+       /// bytes are the block height, the next 3 the transaction index within the block, and the
+       /// final two the output within the transaction.
+       fn get_chain_utxo(&self, genesis_hash: Sha256dHash, unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError>;
 }
 
 /// An interface to send a transaction to the Bitcoin network.
@@ -69,6 +88,7 @@ pub trait FeeEstimator: Sync + Send {
 /// Utility to capture some common parts of ChainWatchInterface implementors.
 /// Keeping a local copy of this in a ChainWatchInterface implementor is likely useful.
 pub struct ChainWatchInterfaceUtil {
+       network: Network,
        watched: Mutex<(Vec<Script>, Vec<(Sha256dHash, u32)>, bool)>, //TODO: Something clever to optimize this
        listeners: Mutex<Vec<Weak<ChainListener>>>,
        reentered: AtomicUsize,
@@ -99,11 +119,19 @@ impl ChainWatchInterface for ChainWatchInterfaceUtil {
                let mut vec = self.listeners.lock().unwrap();
                vec.push(listener);
        }
+
+       fn get_chain_utxo(&self, genesis_hash: Sha256dHash, _unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> {
+               if genesis_hash != genesis_block(self.network).header.bitcoin_hash() {
+                       return Err(ChainError::NotWatched);
+               }
+               Err(ChainError::NotSupported)
+       }
 }
 
 impl ChainWatchInterfaceUtil {
-       pub fn new(logger: Arc<Logger>) -> ChainWatchInterfaceUtil {
+       pub fn new(network: Network, logger: Arc<Logger>) -> ChainWatchInterfaceUtil {
                ChainWatchInterfaceUtil {
+                       network: network,
                        watched: Mutex::new((Vec::new(), Vec::new(), false)),
                        listeners: Mutex::new(Vec::new()),
                        reentered: AtomicUsize::new(1),
index e329b7e696c4b08aec2889797064f6f2a757725f..e249913ec579efbe11616e02f57b8e5aff7e0c4e 100644 (file)
@@ -4,7 +4,7 @@ use secp256k1;
 
 use bitcoin::util::hash::Sha256dHash;
 
-use chain::chaininterface::ChainWatchInterface;
+use chain::chaininterface::{ChainError, ChainWatchInterface};
 use ln::channelmanager;
 use ln::msgs::{ErrorAction,HandleError,RoutingMessageHandler,MsgEncodable,NetAddress,GlobalFeatures};
 use ln::msgs;
@@ -203,13 +203,25 @@ impl RoutingMessageHandler for Router {
                secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.bitcoin_signature_1, &msg.contents.bitcoin_key_1);
                secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.bitcoin_signature_2, &msg.contents.bitcoin_key_2);
 
-               //TODO: Call blockchain thing to ask if the short_channel_id is valid
-               //TODO: Only allow bitcoin chain_hash
-
                if msg.contents.features.requires_unknown_bits() {
                        panic!("Unknown-required-features ChannelAnnouncements should never deserialize!");
                }
 
+               match self.chain_monitor.get_chain_utxo(msg.contents.chain_hash, msg.contents.short_channel_id) {
+                       Ok((script_pubkey, _value)) => {
+                               //TODO: Check if script_pubkey matches bitcoin_key_1 and bitcoin_key_2
+                       },
+                       Err(ChainError::NotSupported) => {
+                               // Tenatively accept, potentially exposing us to DoS attacks
+                       },
+                       Err(ChainError::NotWatched) => {
+                               return Err(HandleError{err: "Channel announced on an unknown chain", action: Some(ErrorAction::IgnoreError)});
+                       },
+                       Err(ChainError::UnknownTx) => {
+                               return Err(HandleError{err: "Channel announced without corresponding UTXO entry", action: Some(ErrorAction::IgnoreError)});
+                       },
+               }
+
                let mut network = self.network_map.write().unwrap();
 
                match network.channels.entry(NetworkMap::get_key(msg.contents.short_channel_id, msg.contents.chain_hash)) {
@@ -643,6 +655,7 @@ mod tests {
        use util::logger::Logger;
 
        use bitcoin::util::hash::Sha256dHash;
+       use bitcoin::network::constants::Network;
 
        use hex;