Parameterize ChannelManager::new with a block hash
authorJeffrey Czyz <jkczyz@gmail.com>
Wed, 3 Mar 2021 19:24:55 +0000 (11:24 -0800)
committerJeffrey Czyz <jkczyz@gmail.com>
Fri, 5 Mar 2021 23:44:54 +0000 (15:44 -0800)
When ChannelMonitors are persisted, they need to store the most recent
block hash seen. However, for newly created channels the default block
hash is used. If persisted before a block is connected, the funding
output may be missed when syncing after a restart. Instead, initialize
ChannelManager with a "birthday" hash so it can be used later when
creating channels.

background-processor/src/lib.rs
fuzz/src/chanmon_consistency.rs
fuzz/src/full_stack.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_test_utils.rs

index 0c7191fbad2bd74fc8ebeafebfe044dbfa97ab96..00f984580f2556fae736a4b7c153caf23839ff58 100644 (file)
@@ -106,7 +106,7 @@ mod tests {
        use lightning::chain::keysinterface::{Sign, InMemorySigner, KeysInterface, KeysManager};
        use lightning::chain::transaction::OutPoint;
        use lightning::get_event_msg;
-       use lightning::ln::channelmanager::{ChannelManager, SimpleArcChannelManager};
+       use lightning::ln::channelmanager::{ChainParameters, ChannelManager, SimpleArcChannelManager};
        use lightning::ln::features::InitFeatures;
        use lightning::ln::msgs::ChannelMessageHandler;
        use lightning::util::config::UserConfig;
@@ -155,10 +155,16 @@ mod tests {
                        let persister = Arc::new(FilesystemPersister::new(format!("{}_persister_{}", persist_dir, i)));
                        let seed = [i as u8; 32];
                        let network = Network::Testnet;
-                       let now = Duration::from_secs(genesis_block(network).header.time as u64);
+                       let genesis_block = genesis_block(network);
+                       let now = Duration::from_secs(genesis_block.header.time as u64);
                        let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_nanos()));
                        let chain_monitor = Arc::new(chainmonitor::ChainMonitor::new(Some(chain_source.clone()), tx_broadcaster.clone(), logger.clone(), fee_estimator.clone(), persister.clone()));
-                       let manager = Arc::new(ChannelManager::new(Network::Testnet, fee_estimator.clone(), chain_monitor.clone(), tx_broadcaster, logger.clone(), keys_manager.clone(), UserConfig::default(), i));
+                       let params = ChainParameters {
+                               network,
+                               latest_hash: genesis_block.block_hash(),
+                               latest_height: 0,
+                       };
+                       let manager = Arc::new(ChannelManager::new(fee_estimator.clone(), chain_monitor.clone(), tx_broadcaster, logger.clone(), keys_manager.clone(), UserConfig::default(), params));
                        let node = Node { node: manager, persister, logger };
                        nodes.push(node);
                }
index d6a106bb7d01a80d632193b32c74b89bc8d21a04..3e205c07183d402fe045110d562f94b084224155 100644 (file)
@@ -19,6 +19,7 @@
 //! channel being force-closed.
 
 use bitcoin::blockdata::block::BlockHeader;
+use bitcoin::blockdata::constants::genesis_block;
 use bitcoin::blockdata::transaction::{Transaction, TxOut};
 use bitcoin::blockdata::script::{Builder, Script};
 use bitcoin::blockdata::opcodes;
@@ -35,7 +36,7 @@ use lightning::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdateErr,
 use lightning::chain::transaction::OutPoint;
 use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
 use lightning::chain::keysinterface::{KeysInterface, InMemorySigner};
-use lightning::ln::channelmanager::{ChannelManager, PaymentHash, PaymentPreimage, PaymentSecret, PaymentSendFailure, ChannelManagerReadArgs};
+use lightning::ln::channelmanager::{ChainParameters, ChannelManager, PaymentHash, PaymentPreimage, PaymentSecret, PaymentSendFailure, ChannelManagerReadArgs};
 use lightning::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
 use lightning::ln::msgs::{CommitmentUpdate, ChannelMessageHandler, DecodeError, ErrorAction, UpdateAddHTLC, Init};
 use lightning::util::enforcing_trait_impls::{EnforcingSigner, INITIAL_REVOKED_COMMITMENT_NUMBER};
@@ -318,7 +319,13 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
                        config.channel_options.fee_proportional_millionths = 0;
                        config.channel_options.announced_channel = true;
                        config.peer_channel_config_limits.min_dust_limit_satoshis = 0;
-                       (ChannelManager::new(Network::Bitcoin, fee_est.clone(), monitor.clone(), broadcast.clone(), Arc::clone(&logger), keys_manager.clone(), config, 0),
+                       let network = Network::Bitcoin;
+                       let params = ChainParameters {
+                               network,
+                               latest_hash: genesis_block(network).block_hash(),
+                               latest_height: 0,
+                       };
+                       (ChannelManager::new(fee_est.clone(), monitor.clone(), broadcast.clone(), Arc::clone(&logger), keys_manager.clone(), config, params),
                        monitor, keys_manager)
                } }
        }
index 3774d0dce839848cb7034fbf041da52c78e60142..132ff95f6bbb6ccafe1f18a893f05b625ad4120c 100644 (file)
@@ -31,7 +31,7 @@ use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget,
 use lightning::chain::chainmonitor;
 use lightning::chain::transaction::OutPoint;
 use lightning::chain::keysinterface::{InMemorySigner, KeysInterface};
-use lightning::ln::channelmanager::{ChannelManager, PaymentHash, PaymentPreimage, PaymentSecret};
+use lightning::ln::channelmanager::{ChainParameters, ChannelManager, PaymentHash, PaymentPreimage, PaymentSecret};
 use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor};
 use lightning::ln::msgs::DecodeError;
 use lightning::routing::router::get_route;
@@ -348,9 +348,16 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
        config.channel_options.fee_proportional_millionths =  slice_to_be32(get_slice!(4));
        config.channel_options.announced_channel = get_slice!(1)[0] != 0;
        config.peer_channel_config_limits.min_dust_limit_satoshis = 0;
-       let channelmanager = Arc::new(ChannelManager::new(Network::Bitcoin, fee_est.clone(), monitor.clone(), broadcast.clone(), Arc::clone(&logger), keys_manager.clone(), config, 0));
+       let network = Network::Bitcoin;
+       let genesis_hash = genesis_block(network).block_hash();
+       let params = ChainParameters {
+               network,
+               latest_hash: genesis_hash,
+               latest_height: 0,
+       };
+       let channelmanager = Arc::new(ChannelManager::new(fee_est.clone(), monitor.clone(), broadcast.clone(), Arc::clone(&logger), keys_manager.clone(), config, params));
        let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret());
-       let net_graph_msg_handler = Arc::new(NetGraphMsgHandler::new(genesis_block(Network::Bitcoin).header.block_hash(), None, Arc::clone(&logger)));
+       let net_graph_msg_handler = Arc::new(NetGraphMsgHandler::new(genesis_hash, None, Arc::clone(&logger)));
 
        let peers = RefCell::new([false; 256]);
        let mut loss_detector = MoneyLossDetector::new(&peers, channelmanager.clone(), monitor.clone(), PeerManager::new(MessageHandler {
index 43bee89dc2867a208a76ce67f534c451bee0c09b..0940cda2e2d66017a1eb84f78eb7b7725da96bcf 100644 (file)
@@ -461,6 +461,24 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
        logger: L,
 }
 
+/// Chain-related parameters used to construct a new `ChannelManager`.
+///
+/// Typically, the block-specific parameters are derived from the best block hash for the network,
+/// as a newly constructed `ChannelManager` will not have created any channels yet. These parameters
+/// are not needed when deserializing a previously constructed `ChannelManager`.
+pub struct ChainParameters {
+       /// The network for determining the `chain_hash` in Lightning messages.
+       pub network: Network,
+
+       /// The hash of the latest block successfully connected.
+       pub latest_hash: BlockHash,
+
+       /// The height of the latest block successfully connected.
+       ///
+       /// Used to track on-chain channel funding outputs and send payments with reliable timelocks.
+       pub latest_height: usize,
+}
+
 /// Whenever we release the `ChannelManager`'s `total_consistency_lock`, from read mode, it is
 /// desirable to notify any listeners on `wait_timeout`/`wait` that new updates are available for
 /// persistence. Therefore, this struct is responsible for locking the total consistency lock and,
@@ -770,24 +788,22 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
        ///
        /// panics if channel_value_satoshis is >= `MAX_FUNDING_SATOSHIS`!
        ///
-       /// Users must provide the current blockchain height from which to track onchain channel
-       /// funding outpoints and send payments with reliable timelocks.
-       ///
        /// Users need to notify the new ChannelManager when a new block is connected or
-       /// disconnected using its `block_connected` and `block_disconnected` methods.
-       pub fn new(network: Network, fee_est: F, chain_monitor: M, tx_broadcaster: T, logger: L, keys_manager: K, config: UserConfig, current_blockchain_height: usize) -> Self {
+       /// disconnected using its `block_connected` and `block_disconnected` methods, starting
+       /// from after `params.latest_hash`.
+       pub fn new(fee_est: F, chain_monitor: M, tx_broadcaster: T, logger: L, keys_manager: K, config: UserConfig, params: ChainParameters) -> Self {
                let mut secp_ctx = Secp256k1::new();
                secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes());
 
                ChannelManager {
                        default_configuration: config.clone(),
-                       genesis_hash: genesis_block(network).header.block_hash(),
+                       genesis_hash: genesis_block(params.network).header.block_hash(),
                        fee_estimator: fee_est,
                        chain_monitor,
                        tx_broadcaster,
 
-                       latest_block_height: AtomicUsize::new(current_blockchain_height),
-                       last_block_hash: Mutex::new(Default::default()),
+                       latest_block_height: AtomicUsize::new(params.latest_height),
+                       last_block_hash: Mutex::new(params.latest_hash),
                        secp_ctx,
 
                        channel_state: Mutex::new(ChannelHolder{
index cac714c4ce271925d6670fee7b3eeb6893978160..e7204a045abd8a673bc49f0f782b8c06900c2b3f 100644 (file)
@@ -13,7 +13,7 @@
 use chain::Watch;
 use chain::channelmonitor::ChannelMonitor;
 use chain::transaction::OutPoint;
-use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentPreimage, PaymentHash, PaymentSecret, PaymentSendFailure};
+use ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentPreimage, PaymentHash, PaymentSecret, PaymentSendFailure};
 use routing::router::{Route, get_route};
 use routing::network_graph::{NetGraphMsgHandler, NetworkGraph};
 use ln::features::InitFeatures;
@@ -28,6 +28,7 @@ use util::config::UserConfig;
 use util::ser::{ReadableArgs, Writeable, Readable};
 
 use bitcoin::blockdata::block::{Block, BlockHeader};
+use bitcoin::blockdata::constants::genesis_block;
 use bitcoin::blockdata::transaction::{Transaction, TxOut};
 use bitcoin::network::constants::Network;
 
@@ -1163,7 +1164,13 @@ pub fn create_node_chanmgrs<'a, 'b>(node_count: usize, cfgs: &'a Vec<NodeCfg<'b>
                default_config.channel_options.announced_channel = true;
                default_config.peer_channel_config_limits.force_announced_channel_preference = false;
                default_config.own_channel_config.our_htlc_minimum_msat = 1000; // sanitization being done by the sender, to exerce receiver logic we need to lift of limit
-               let node = ChannelManager::new(Network::Testnet, cfgs[i].fee_estimator, &cfgs[i].chain_monitor, cfgs[i].tx_broadcaster, cfgs[i].logger, cfgs[i].keys_manager, if node_config[i].is_some() { node_config[i].clone().unwrap() } else { default_config }, 0);
+               let network = Network::Testnet;
+               let params = ChainParameters {
+                       network,
+                       latest_hash: genesis_block(network).header.block_hash(),
+                       latest_height: 0,
+               };
+               let node = ChannelManager::new(cfgs[i].fee_estimator, &cfgs[i].chain_monitor, cfgs[i].tx_broadcaster, cfgs[i].logger, cfgs[i].keys_manager, if node_config[i].is_some() { node_config[i].clone().unwrap() } else { default_config }, params);
                chanmgrs.push(node);
        }