From: Jeffrey Czyz Date: Thu, 30 Jul 2020 17:27:41 +0000 (-0700) Subject: Replace WatchEventProvider with chain::Notify X-Git-Url: http://git.bitcoin.ninja/?a=commitdiff_plain;h=5d5095ef168d81d10c1f21308e406771f041b493;p=rust-lightning Replace WatchEventProvider with chain::Notify WatchEventProvider served as a means for replacing ChainWatchInterface. However, it requires users to explicitly fetch WatchEvents, even if not interested in them. Replace WatchEventProvider by chain::Notify, which is an optional member of ChainMonitor. If set, interesting transactions and output spends are registered such that blocks containing them can be retrieved from a chain source in an efficient manner. This is useful when the chain source is not a full node. For Electrum, it allows for pre-filtered blocks. For BIP157/158, it serves as a means to match against compact filters. --- diff --git a/ARCH.md b/ARCH.md index b7276669a..a1c9e37b4 100644 --- a/ARCH.md +++ b/ARCH.md @@ -54,9 +54,9 @@ At a high level, some of the common interfaces fit together as follows: | ----------------- \ _---------------- / / | | chain::Access | \ / | ChainMonitor |--------------- | ----------------- \ / ---------------- - | | \ / -(as RoutingMessageHandler) v v - \ -------------------- --------- - -----------------> | NetGraphMsgHandler | | Event | - -------------------- --------- + | | \ / | +(as RoutingMessageHandler) v v v + \ -------------------- --------- ----------------- + -----------------> | NetGraphMsgHandler | | Event | | chain::Notify | + -------------------- --------- ----------------- ``` diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 43a4fb2aa..7ef635923 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -75,7 +75,7 @@ impl Writer for VecWriter { struct TestChainMonitor { pub logger: Arc, - pub chain_monitor: Arc, Arc, Arc>>, + pub chain_monitor: Arc, Arc, Arc, Arc>>, pub update_ret: Mutex>, // If we reload a node with an old copy of ChannelMonitors, the ChannelManager deserialization // logic will automatically force-close our channels for us (as we don't have an up-to-date @@ -88,7 +88,7 @@ struct TestChainMonitor { impl TestChainMonitor { pub fn new(broadcaster: Arc, logger: Arc, feeest: Arc) -> Self { Self { - chain_monitor: Arc::new(channelmonitor::ChainMonitor::new(broadcaster, logger.clone(), feeest)), + chain_monitor: Arc::new(channelmonitor::ChainMonitor::new(None, broadcaster, logger.clone(), feeest)), logger, update_ret: Mutex::new(Ok(())), latest_monitors: Mutex::new(HashMap::new()), diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index a869078f6..908e14599 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -137,13 +137,13 @@ impl<'a> std::hash::Hash for Peer<'a> { type ChannelMan = ChannelManager< EnforcingChannelKeys, - Arc, Arc, Arc>>, + Arc, Arc, Arc, Arc>>, Arc, Arc, Arc, Arc>; type PeerMan<'a> = PeerManager, Arc, Arc, Arc>>, Arc>; struct MoneyLossDetector<'a> { manager: Arc, - monitor: Arc, Arc, Arc>>, + monitor: Arc, Arc, Arc, Arc>>, handler: PeerMan<'a>, peers: &'a RefCell<[bool; 256]>, @@ -157,7 +157,7 @@ struct MoneyLossDetector<'a> { impl<'a> MoneyLossDetector<'a> { pub fn new(peers: &'a RefCell<[bool; 256]>, manager: Arc, - monitor: Arc, Arc, Arc>>, + monitor: Arc, Arc, Arc, Arc>>, handler: PeerMan<'a>) -> Self { MoneyLossDetector { manager, @@ -331,7 +331,7 @@ pub fn do_test(data: &[u8], logger: &Arc) { }; let broadcast = Arc::new(TestBroadcaster{}); - let monitor = Arc::new(channelmonitor::ChainMonitor::new(broadcast.clone(), Arc::clone(&logger), fee_est.clone())); + let monitor = Arc::new(channelmonitor::ChainMonitor::new(None, broadcast.clone(), Arc::clone(&logger), fee_est.clone())); let keys_manager = Arc::new(KeyProvider { node_secret: our_network_key.clone(), counter: AtomicU64::new(0) }); let mut config = UserConfig::default(); diff --git a/lightning-net-tokio/src/lib.rs b/lightning-net-tokio/src/lib.rs index c71f435be..b5282db88 100644 --- a/lightning-net-tokio/src/lib.rs +++ b/lightning-net-tokio/src/lib.rs @@ -26,7 +26,8 @@ //! type FeeEstimator = dyn lightning::chain::chaininterface::FeeEstimator; //! type Logger = dyn lightning::util::logger::Logger; //! type ChainAccess = dyn lightning::chain::Access; -//! type ChainMonitor = lightning::ln::channelmonitor::ChainMonitor, Arc, Arc>; +//! type ChainNotify = dyn lightning::chain::Notify; +//! type ChainMonitor = lightning::ln::channelmonitor::ChainMonitor, Arc, Arc, Arc>; //! type ChannelManager = lightning::ln::channelmanager::SimpleArcChannelManager; //! type PeerManager = lightning::ln::peer_handler::SimpleArcPeerManager; //! diff --git a/lightning/src/chain/mod.rs b/lightning/src/chain/mod.rs index 0a63a4c5a..85a9da084 100644 --- a/lightning/src/chain/mod.rs +++ b/lightning/src/chain/mod.rs @@ -79,32 +79,22 @@ pub trait Watch: Send + Sync { fn release_pending_htlc_updates(&self) -> Vec; } -/// An interface for providing [`WatchEvent`]s. +/// The `Notify` trait defines behavior for indicating chain activity of interest pertaining to +/// channels. /// -/// [`WatchEvent`]: enum.WatchEvent.html -pub trait WatchEventProvider { - /// Releases events produced since the last call. Subsequent calls must only return new events. - fn release_pending_watch_events(&self) -> Vec; -} - -/// An event indicating on-chain activity to watch for pertaining to a channel. -pub enum WatchEvent { - /// Watch for a transaction with `txid` and having an output with `script_pubkey` as a spending - /// condition. - WatchTransaction { - /// Identifier of the transaction. - txid: Txid, - - /// Spending condition for an output of the transaction. - script_pubkey: Script, - }, - /// Watch for spends of a transaction output identified by `outpoint` having `script_pubkey` as - /// the spending condition. - WatchOutput { - /// Identifier for the output. - outpoint: OutPoint, +/// This is useful in order to have a [`Watch`] implementation convey to a chain source which +/// transactions to be notified of. This may take the form of pre-filtering blocks or, in the case +/// of [BIP 157]/[BIP 158], only fetching a block if the compact filter matches. +/// +/// [`Watch`]: trait.Watch.html +/// [BIP 157]: https://github.com/bitcoin/bips/blob/master/bip-0157.mediawiki +/// [BIP 158]: https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki +pub trait Notify: Send + Sync { + /// Registers interest in a transaction with `txid` and having an output with `script_pubkey` as + /// a spending condition. + fn register_tx(&self, txid: Txid, script_pubkey: Script); - /// Spending condition for the output. - script_pubkey: Script, - } + /// Registers interest in spends of a transaction output identified by `outpoint` having + /// `script_pubkey` as the spending condition. + fn register_output(&self, outpoint: OutPoint, script_pubkey: Script); } diff --git a/lightning/src/ln/channelmonitor.rs b/lightning/src/ln/channelmonitor.rs index 2baf32725..0f41945bd 100644 --- a/lightning/src/ln/channelmonitor.rs +++ b/lightning/src/ln/channelmonitor.rs @@ -35,6 +35,7 @@ use ln::chan_utils::{CounterpartyCommitmentSecrets, HTLCOutputInCommitment, Loca use ln::channelmanager::{HTLCSource, PaymentPreimage, PaymentHash}; use ln::onchaintx::{OnchainTxHandler, InputDescriptors}; use chain; +use chain::Notify; use chain::chaininterface::{ChainWatchedUtil, BroadcasterInterface, FeeEstimator}; use chain::transaction::OutPoint; use chain::keysinterface::{SpendableOutputDescriptor, ChannelKeys}; @@ -160,9 +161,9 @@ impl_writeable!(HTLCUpdate, 0, { payment_hash, payment_preimage, source }); /// independently to monitor channels remotely. /// /// [`chain::Watch`]: ../../chain/trait.Watch.html -/// [`ChannelManager`]: ../channelmanager/struct.ChannelManager.html -pub struct ChainMonitor - where T::Target: BroadcasterInterface, +pub struct ChainMonitor + where C::Target: chain::Notify, + T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, { @@ -170,18 +171,41 @@ pub struct ChainMonitor pub monitors: Mutex>>, #[cfg(not(test))] monitors: Mutex>>, - watch_events: Mutex, + watch_events: Mutex, + chain_source: Option, broadcaster: T, logger: L, fee_estimator: F } -struct WatchEventQueue { +struct WatchEventCache { watched: ChainWatchedUtil, - events: Vec, + events: Vec, } -impl WatchEventQueue { +/// An event indicating on-chain activity to watch for pertaining to a channel. +enum WatchEvent { + /// Watch for a transaction with `txid` and having an output with `script_pubkey` as a spending + /// condition. + WatchTransaction { + /// Identifier of the transaction. + txid: Txid, + + /// Spending condition for an output of the transaction. + script_pubkey: Script, + }, + /// Watch for spends of a transaction output identified by `outpoint` having `script_pubkey` as + /// the spending condition. + WatchOutput { + /// Identifier for the output. + outpoint: OutPoint, + + /// Spending condition for the output. + script_pubkey: Script, + } +} + +impl WatchEventCache { fn new() -> Self { Self { watched: ChainWatchedUtil::new(), @@ -191,7 +215,7 @@ impl WatchEventQueue { fn watch_tx(&mut self, txid: &Txid, script_pubkey: &Script) { if self.watched.register_tx(txid, script_pubkey) { - self.events.push(chain::WatchEvent::WatchTransaction { + self.events.push(WatchEvent::WatchTransaction { txid: *txid, script_pubkey: script_pubkey.clone() }); @@ -201,7 +225,7 @@ impl WatchEventQueue { fn watch_output(&mut self, outpoint: (&Txid, usize), script_pubkey: &Script) { let (txid, index) = outpoint; if self.watched.register_outpoint((*txid, index as u32), script_pubkey) { - self.events.push(chain::WatchEvent::WatchOutput { + self.events.push(WatchEvent::WatchOutput { outpoint: OutPoint { txid: *txid, index: index as u16, @@ -211,24 +235,43 @@ impl WatchEventQueue { } } - fn dequeue_events(&mut self) -> Vec { - let mut pending_events = Vec::with_capacity(self.events.len()); - pending_events.append(&mut self.events); - pending_events + fn flush_events(&mut self, chain_source: &Option) -> bool where C::Target: chain::Notify { + let num_events = self.events.len(); + match chain_source { + &None => self.events.clear(), + &Some(ref chain_source) => { + for event in self.events.drain(..) { + match event { + WatchEvent::WatchTransaction { txid, script_pubkey } => { + chain_source.register_tx(txid, script_pubkey) + }, + WatchEvent::WatchOutput { outpoint, script_pubkey } => { + chain_source.register_output(outpoint, script_pubkey) + }, + } + } + } + } + num_events > 0 } } -impl ChainMonitor - where T::Target: BroadcasterInterface, +impl ChainMonitor + where C::Target: chain::Notify, + T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, { /// Delegates to [`ChannelMonitor::block_connected`] for each watched channel. Any HTLCs that /// were resolved on chain will be retuned by [`chain::Watch::release_pending_htlc_updates`]. /// + /// Calls back to [`chain::Notify`] if any monitor indicated new outputs to watch, returning + /// `true` if so. + /// /// [`ChannelMonitor::block_connected`]: struct.ChannelMonitor.html#method.block_connected /// [`chain::Watch::release_pending_htlc_updates`]: ../../chain/trait.Watch.html#tymethod.release_pending_htlc_updates - pub fn block_connected(&self, header: &BlockHeader, txdata: &[(usize, &Transaction)], height: u32) { + /// [`chain::Notify`]: ../../chain/trait.Notify.html + pub fn block_connected(&self, header: &BlockHeader, txdata: &[(usize, &Transaction)], height: u32) -> bool { let mut watch_events = self.watch_events.lock().unwrap(); let matched_txn: Vec<_> = txdata.iter().filter(|&&(_, tx)| watch_events.watched.does_match_tx(tx)).map(|e| *e).collect(); { @@ -243,6 +286,7 @@ impl ChainMonitor ChainMonitor ChainMonitor - where T::Target: BroadcasterInterface, +impl ChainMonitor + where C::Target: chain::Notify, + T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, { /// Creates a new object which can be used to monitor several channels given the chain /// interface with which to register to receive notifications. - pub fn new(broadcaster: T, logger: L, feeest: F) -> Self { + pub fn new(chain_source: Option, broadcaster: T, logger: L, feeest: F) -> Self { Self { monitors: Mutex::new(HashMap::new()), - watch_events: Mutex::new(WatchEventQueue::new()), + watch_events: Mutex::new(WatchEventCache::new()), + chain_source, broadcaster, logger, fee_estimator: feeest, @@ -274,6 +320,10 @@ impl ChainMonitor) -> Result<(), MonitorUpdateError> { let mut watch_events = self.watch_events.lock().unwrap(); let mut monitors = self.monitors.lock().unwrap(); @@ -293,6 +343,7 @@ impl ChainMonitor ChainMonitor chain::Watch for ChainMonitor - where T::Target: BroadcasterInterface, +impl chain::Watch for ChainMonitor + where C::Target: chain::Notify, + T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, { @@ -339,8 +391,9 @@ impl events::EventsProvider for ChainMonitor - where T::Target: BroadcasterInterface, +impl events::EventsProvider for ChainMonitor + where C::Target: chain::Notify, + T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, { @@ -353,16 +406,6 @@ impl events::EventsProvid } } -impl chain::WatchEventProvider for ChainMonitor - where T::Target: BroadcasterInterface, - F::Target: FeeEstimator, - L::Target: Logger, -{ - fn release_pending_watch_events(&self) -> Vec { - self.watch_events.lock().unwrap().dequeue_events() - } -} - /// If an HTLC expires within this many blocks, don't try to claim it in a shared transaction, /// instead claiming it in its own individual transaction. pub(crate) const CLTV_SHARED_CLAIM_BUFFER: u32 = 12; diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 8d60af476..d0ee5f672 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -1,7 +1,6 @@ //! A bunch of useful utilities for building networks of nodes and exchanging messages between //! nodes for functional tests. -use chain; use chain::Watch; use chain::transaction::OutPoint; use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentPreimage, PaymentHash, PaymentSecret, PaymentSendFailure}; @@ -73,28 +72,11 @@ pub fn connect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, depth: u32, he } pub fn connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: &Block, height: u32) { - use chain::WatchEventProvider; - - let watch_events = node.chain_monitor.chain_monitor.release_pending_watch_events(); - process_chain_watch_events(&watch_events); - let txdata: Vec<_> = block.txdata.iter().enumerate().collect(); - loop { - node.chain_monitor.chain_monitor.block_connected(&block.header, &txdata, height); - - let watch_events = node.chain_monitor.chain_monitor.release_pending_watch_events(); - process_chain_watch_events(&watch_events); - - if watch_events.is_empty() { - break; - } - } - + while node.chain_monitor.chain_monitor.block_connected(&block.header, &txdata, height) {} node.node.block_connected(&block.header, &txdata, height); } -fn process_chain_watch_events(_events: &Vec) {} - pub fn disconnect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, header: &BlockHeader, height: u32) { node.chain_monitor.chain_monitor.block_disconnected(header, height); node.node.block_disconnected(header, height); @@ -207,7 +189,7 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> { }).unwrap(); } - let channel_monitor = test_utils::TestChainMonitor::new(self.tx_broadcaster.clone(), &self.logger, &feeest); + let channel_monitor = test_utils::TestChainMonitor::new(Some(self.chain_source), self.tx_broadcaster.clone(), &self.logger, &feeest); for deserialized_monitor in deserialized_monitors.drain(..) { if let Err(_) = channel_monitor.watch_channel(deserialized_monitor.get_funding_txo().0, deserialized_monitor) { panic!(); @@ -1112,7 +1094,7 @@ pub fn create_node_cfgs<'a>(node_count: usize, chanmon_cfgs: &'a Vec)>::read(&mut chan_0_monitor_read).unwrap(); @@ -4381,7 +4381,7 @@ fn test_manager_serialize_deserialize_events() { fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: 253 }; logger = test_utils::TestLogger::new(); - new_chain_monitor = test_utils::TestChainMonitor::new(nodes[0].tx_broadcaster.clone(), &logger, &fee_estimator); + new_chain_monitor = test_utils::TestChainMonitor::new(Some(nodes[0].chain_source), nodes[0].tx_broadcaster.clone(), &logger, &fee_estimator); nodes[0].chain_monitor = &new_chain_monitor; let mut chan_0_monitor_read = &chan_0_monitor_serialized.0[..]; let (_, mut chan_0_monitor) = <(BlockHash, ChannelMonitor)>::read(&mut chan_0_monitor_read).unwrap(); @@ -4471,7 +4471,7 @@ fn test_simple_manager_serialize_deserialize() { logger = test_utils::TestLogger::new(); fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: 253 }; - new_chain_monitor = test_utils::TestChainMonitor::new(nodes[0].tx_broadcaster.clone(), &logger, &fee_estimator); + new_chain_monitor = test_utils::TestChainMonitor::new(Some(nodes[0].chain_source), nodes[0].tx_broadcaster.clone(), &logger, &fee_estimator); nodes[0].chain_monitor = &new_chain_monitor; let mut chan_0_monitor_read = &chan_0_monitor_serialized.0[..]; let (_, mut chan_0_monitor) = <(BlockHash, ChannelMonitor)>::read(&mut chan_0_monitor_read).unwrap(); @@ -4549,7 +4549,7 @@ fn test_manager_serialize_deserialize_inconsistent_monitor() { logger = test_utils::TestLogger::new(); fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: 253 }; - new_chain_monitor = test_utils::TestChainMonitor::new(nodes[0].tx_broadcaster.clone(), &logger, &fee_estimator); + new_chain_monitor = test_utils::TestChainMonitor::new(Some(nodes[0].chain_source), nodes[0].tx_broadcaster.clone(), &logger, &fee_estimator); nodes[0].chain_monitor = &new_chain_monitor; let mut node_0_stale_monitors = Vec::new(); @@ -5660,7 +5660,7 @@ fn test_key_derivation_params() { // We manually create the node configuration to backup the seed. let seed = [42; 32]; let keys_manager = test_utils::TestKeysInterface::new(&seed, Network::Testnet); - let chain_monitor = test_utils::TestChainMonitor::new(&chanmon_cfgs[0].tx_broadcaster, &chanmon_cfgs[0].logger, &chanmon_cfgs[0].fee_estimator); + let chain_monitor = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[0].chain_source), &chanmon_cfgs[0].tx_broadcaster, &chanmon_cfgs[0].logger, &chanmon_cfgs[0].fee_estimator); let node = NodeCfg { chain_source: &chanmon_cfgs[0].chain_source, logger: &chanmon_cfgs[0].logger, tx_broadcaster: &chanmon_cfgs[0].tx_broadcaster, fee_estimator: &chanmon_cfgs[0].fee_estimator, chain_monitor, keys_manager, node_seed: seed }; let mut node_cfgs = create_node_cfgs(3, &chanmon_cfgs); node_cfgs.remove(0); @@ -7532,7 +7532,7 @@ fn test_data_loss_protect() { tx_broadcaster = test_utils::TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new())}; fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: 253 }; keys_manager = test_utils::TestKeysInterface::new(&nodes[0].node_seed, Network::Testnet); - monitor = test_utils::TestChainMonitor::new(&tx_broadcaster, &logger, &fee_estimator); + monitor = test_utils::TestChainMonitor::new(Some(&chain_source), &tx_broadcaster, &logger, &fee_estimator); node_state_0 = { let mut channel_monitors = HashMap::new(); channel_monitors.insert(OutPoint { txid: chan.3.txid(), index: 0 }, &mut chain_monitor); @@ -8359,7 +8359,7 @@ fn test_update_err_monitor_lockdown() { let new_monitor = <(BlockHash, channelmonitor::ChannelMonitor)>::read( &mut ::std::io::Cursor::new(&w.0)).unwrap().1; assert!(new_monitor == *monitor); - let watchtower = test_utils::TestChainMonitor::new(&chanmon_cfgs[0].tx_broadcaster, &logger, &chanmon_cfgs[0].fee_estimator); + let watchtower = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[0].chain_source), &chanmon_cfgs[0].tx_broadcaster, &logger, &chanmon_cfgs[0].fee_estimator); assert!(watchtower.watch_channel(outpoint, new_monitor).is_ok()); watchtower }; diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index eb1db2028..3473d9dda 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -19,7 +19,7 @@ use bitcoin::blockdata::transaction::{Transaction, TxOut}; use bitcoin::blockdata::script::{Builder, Script}; use bitcoin::blockdata::opcodes; use bitcoin::network::constants::Network; -use bitcoin::hash_types::BlockHash; +use bitcoin::hash_types::{BlockHash, Txid}; use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1, Signature}; @@ -54,18 +54,18 @@ impl chaininterface::FeeEstimator for TestFeeEstimator { pub struct TestChainMonitor<'a> { pub added_monitors: Mutex)>>, pub latest_monitor_update_id: Mutex>, - pub chain_monitor: channelmonitor::ChainMonitor, + pub chain_monitor: channelmonitor::ChainMonitor, pub update_ret: Mutex>, // If this is set to Some(), after the next return, we'll always return this until update_ret // is changed: pub next_update_ret: Mutex>>, } impl<'a> TestChainMonitor<'a> { - pub fn new(broadcaster: &'a chaininterface::BroadcasterInterface, logger: &'a TestLogger, fee_estimator: &'a TestFeeEstimator) -> Self { + pub fn new(chain_source: Option<&'a TestChainSource>, broadcaster: &'a chaininterface::BroadcasterInterface, logger: &'a TestLogger, fee_estimator: &'a TestFeeEstimator) -> Self { Self { added_monitors: Mutex::new(Vec::new()), latest_monitor_update_id: Mutex::new(HashMap::new()), - chain_monitor: channelmonitor::ChainMonitor::new(broadcaster, logger, fee_estimator), + chain_monitor: channelmonitor::ChainMonitor::new(chain_source, broadcaster, logger, fee_estimator), update_ret: Mutex::new(Ok(())), next_update_ret: Mutex::new(None), } @@ -408,3 +408,8 @@ impl chain::Access for TestChainSource { self.utxo_ret.lock().unwrap().clone() } } + +impl chain::Notify for TestChainSource { + fn register_tx(&self, _txid: Txid, _script_pubkey: Script) {} + fn register_output(&self, _outpoint: OutPoint, _script_pubkey: Script) {} +}