From 51a3353740126b5f251958285b992a7673c65d1a Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sat, 21 Jan 2023 03:28:35 +0000 Subject: [PATCH] Move `chain::Access` to `routing` and rename it `UtxoLookup` The `chain::Access` trait (and the `chain::AccessError` enum) is a bit strange - it only really makes sense if users import it via the `chain` module, otherwise they're left with a trait just called `Access`. Worse, for bindings users its always just called `Access`, in part because many downstream languages don't have a mechanism to import a module and then refer to it. Further, its stuck dangling in the `chain` top-level mod.rs file, sitting in a module that doesn't use it at all (it's only used in `routing::gossip`). Instead, we give it its full name - `UtxoLookup` (and rename the error enum `UtxoLookupError`) and put it in the a new `routing::utxo` module, next to `routing::gossip`. --- ARCH.md | 6 +- fuzz/src/full_stack.rs | 3 +- fuzz/src/router.rs | 12 ++-- lightning-background-processor/src/lib.rs | 45 ++++++------- lightning-net-tokio/src/lib.rs | 4 +- lightning/src/chain/mod.rs | 21 ------ lightning/src/routing/gossip.rs | 78 +++++++++++------------ lightning/src/routing/mod.rs | 1 + lightning/src/routing/router.rs | 2 +- lightning/src/routing/utxo.rs | 36 +++++++++++ lightning/src/util/test_utils.rs | 12 ++-- 11 files changed, 118 insertions(+), 102 deletions(-) create mode 100644 lightning/src/routing/utxo.rs diff --git a/ARCH.md b/ARCH.md index 9efccd9c..bd5fc1bd 100644 --- a/ARCH.md +++ b/ARCH.md @@ -51,9 +51,9 @@ At a high level, some of the common interfaces fit together as follows: --------------- / (as EventsProvider) ^ | | | PeerManager |- \ | | | --------------- \ | (is-a) | | - | ----------------- \ _---------------- / / - | | chain::Access | \ / | ChainMonitor |--------------- - | ----------------- \ / ---------------- + | -------------- \ _---------------- / / + | | UtxoLookup | \ / | ChainMonitor |--------------- + | -------------- \ / ---------------- | ^ \ / | (as RoutingMessageHandler) | v v \ ----------------- --------- ----------------- diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 145b7c83..57c665a2 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -41,6 +41,7 @@ use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor,Ig use lightning::ln::msgs::{self, DecodeError}; use lightning::ln::script::ShutdownScript; use lightning::routing::gossip::{P2PGossipSync, NetworkGraph}; +use lightning::routing::utxo::UtxoLookup; use lightning::routing::router::{find_route, InFlightHtlcs, PaymentParameters, Route, RouteHop, RouteParameters, Router}; use lightning::routing::scoring::FixedPenaltyScorer; use lightning::util::config::UserConfig; @@ -183,7 +184,7 @@ impl<'a> std::hash::Hash for Peer<'a> { type ChannelMan<'a> = ChannelManager< Arc, Arc, Arc, Arc, Arc>>, Arc, Arc, Arc, Arc, Arc, &'a FuzzRouter, Arc>; -type PeerMan<'a> = PeerManager, Arc>, Arc>>, Arc, Arc>>, IgnoringMessageHandler, Arc, IgnoringMessageHandler, Arc>; +type PeerMan<'a> = PeerManager, Arc>, Arc>>, Arc, Arc>>, IgnoringMessageHandler, Arc, IgnoringMessageHandler, Arc>; struct MoneyLossDetector<'a> { manager: Arc>, diff --git a/fuzz/src/router.rs b/fuzz/src/router.rs index dbf619f1..3d5c88bc 100644 --- a/fuzz/src/router.rs +++ b/fuzz/src/router.rs @@ -11,11 +11,11 @@ use bitcoin::blockdata::script::Builder; use bitcoin::blockdata::transaction::TxOut; use bitcoin::hash_types::BlockHash; -use lightning::chain; use lightning::chain::transaction::OutPoint; use lightning::ln::channelmanager::{self, ChannelDetails, ChannelCounterparty}; use lightning::ln::msgs; use lightning::routing::gossip::{NetworkGraph, RoutingFees}; +use lightning::routing::utxo::{UtxoLookup, UtxoLookupError}; use lightning::routing::router::{find_route, PaymentParameters, RouteHint, RouteHintHop, RouteParameters}; use lightning::routing::scoring::FixedPenaltyScorer; use lightning::util::config::UserConfig; @@ -84,13 +84,13 @@ impl InputData { struct FuzzChainSource { input: Arc, } -impl chain::Access for FuzzChainSource { - fn get_utxo(&self, _genesis_hash: &BlockHash, _short_channel_id: u64) -> Result { +impl UtxoLookup for FuzzChainSource { + fn get_utxo(&self, _genesis_hash: &BlockHash, _short_channel_id: u64) -> Result { match self.input.get_slice(2) { - Some(&[0, _]) => Err(chain::AccessError::UnknownChain), - Some(&[1, _]) => Err(chain::AccessError::UnknownTx), + Some(&[0, _]) => Err(UtxoLookupError::UnknownChain), + Some(&[1, _]) => Err(UtxoLookupError::UnknownTx), Some(&[_, x]) => Ok(TxOut { value: 0, script_pubkey: Builder::new().push_int(x as i64).into_script().to_v0_p2wsh() }), - None => Err(chain::AccessError::UnknownTx), + None => Err(UtxoLookupError::UnknownTx), _ => unreachable!(), } } diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index 087aa174..9b1df752 100644 --- a/lightning-background-processor/src/lib.rs +++ b/lightning-background-processor/src/lib.rs @@ -30,6 +30,7 @@ use lightning::ln::channelmanager::ChannelManager; use lightning::ln::msgs::{ChannelMessageHandler, OnionMessageHandler, RoutingMessageHandler}; use lightning::ln::peer_handler::{CustomMessageHandler, PeerManager, SocketDescriptor}; use lightning::routing::gossip::{NetworkGraph, P2PGossipSync}; +use lightning::routing::utxo::UtxoLookup; use lightning::routing::router::Router; use lightning::routing::scoring::{Score, WriteableScore}; use lightning::util::events::{Event, EventHandler, EventsProvider}; @@ -116,13 +117,13 @@ const FIRST_NETWORK_PRUNE_TIMER: u64 = 1; /// Either [`P2PGossipSync`] or [`RapidGossipSync`]. pub enum GossipSync< - P: Deref>, + P: Deref>, R: Deref>, G: Deref>, - A: Deref, + U: Deref, L: Deref, > -where A::Target: chain::Access, L::Target: Logger { +where U::Target: UtxoLookup, L::Target: Logger { /// Gossip sync via the lightning peer-to-peer network as defined by BOLT 7. P2P(P), /// Rapid gossip sync from a trusted server. @@ -132,13 +133,13 @@ where A::Target: chain::Access, L::Target: Logger { } impl< - P: Deref>, + P: Deref>, R: Deref>, G: Deref>, - A: Deref, + U: Deref, L: Deref, -> GossipSync -where A::Target: chain::Access, L::Target: Logger { +> GossipSync +where U::Target: UtxoLookup, L::Target: Logger { fn network_graph(&self) -> Option<&G> { match self { GossipSync::P2P(gossip_sync) => Some(gossip_sync.network_graph()), @@ -163,10 +164,10 @@ where A::Target: chain::Access, L::Target: Logger { } /// (C-not exported) as the bindings concretize everything and have constructors for us -impl>, G: Deref>, A: Deref, L: Deref> - GossipSync, G, A, L> +impl>, G: Deref>, U: Deref, L: Deref> + GossipSync, G, U, L> where - A::Target: chain::Access, + U::Target: UtxoLookup, L::Target: Logger, { /// Initializes a new [`GossipSync::P2P`] variant. @@ -178,10 +179,10 @@ where /// (C-not exported) as the bindings concretize everything and have constructors for us impl<'a, R: Deref>, G: Deref>, L: Deref> GossipSync< - &P2PGossipSync, + &P2PGossipSync, R, G, - &'a (dyn chain::Access + Send + Sync), + &'a (dyn UtxoLookup + Send + Sync), L, > where @@ -196,10 +197,10 @@ where /// (C-not exported) as the bindings concretize everything and have constructors for us impl<'a, L: Deref> GossipSync< - &P2PGossipSync<&'a NetworkGraph, &'a (dyn chain::Access + Send + Sync), L>, + &P2PGossipSync<&'a NetworkGraph, &'a (dyn UtxoLookup + Send + Sync), L>, &RapidGossipSync<&'a NetworkGraph, L>, &'a NetworkGraph, - &'a (dyn chain::Access + Send + Sync), + &'a (dyn UtxoLookup + Send + Sync), L, > where @@ -397,7 +398,7 @@ macro_rules! define_run_body { #[cfg(feature = "futures")] pub async fn process_events_async< 'a, - CA: 'static + Deref + Send + Sync, + UL: 'static + Deref + Send + Sync, CF: 'static + Deref + Send + Sync, CW: 'static + Deref + Send + Sync, T: 'static + Deref + Send + Sync, @@ -418,7 +419,7 @@ pub async fn process_events_async< PS: 'static + Deref + Send, M: 'static + Deref::Signer, CF, T, F, L, P>> + Send + Sync, CM: 'static + Deref> + Send + Sync, - PGS: 'static + Deref> + Send + Sync, + PGS: 'static + Deref> + Send + Sync, RGS: 'static + Deref> + Send, UMH: 'static + Deref + Send + Sync, PM: 'static + Deref> + Send + Sync, @@ -428,11 +429,11 @@ pub async fn process_events_async< Sleeper: Fn(Duration) -> SleepFuture >( persister: PS, event_handler: EventHandler, chain_monitor: M, channel_manager: CM, - gossip_sync: GossipSync, peer_manager: PM, logger: L, scorer: Option, + gossip_sync: GossipSync, peer_manager: PM, logger: L, scorer: Option, sleeper: Sleeper, ) -> Result<(), io::Error> where - CA::Target: 'static + chain::Access, + UL::Target: 'static + UtxoLookup, CF::Target: 'static + chain::Filter, CW::Target: 'static + chain::Watch<::Signer>, T::Target: 'static + BroadcasterInterface, @@ -531,7 +532,7 @@ impl BackgroundProcessor { /// [`NetworkGraph::write`]: lightning::routing::gossip::NetworkGraph#impl-Writeable pub fn start< 'a, - CA: 'static + Deref + Send + Sync, + UL: 'static + Deref + Send + Sync, CF: 'static + Deref + Send + Sync, CW: 'static + Deref + Send + Sync, T: 'static + Deref + Send + Sync, @@ -551,7 +552,7 @@ impl BackgroundProcessor { PS: 'static + Deref + Send, M: 'static + Deref::Signer, CF, T, F, L, P>> + Send + Sync, CM: 'static + Deref> + Send + Sync, - PGS: 'static + Deref> + Send + Sync, + PGS: 'static + Deref> + Send + Sync, RGS: 'static + Deref> + Send, UMH: 'static + Deref + Send + Sync, PM: 'static + Deref> + Send + Sync, @@ -559,10 +560,10 @@ impl BackgroundProcessor { SC: for <'b> WriteableScore<'b>, >( persister: PS, event_handler: EH, chain_monitor: M, channel_manager: CM, - gossip_sync: GossipSync, peer_manager: PM, logger: L, scorer: Option, + gossip_sync: GossipSync, peer_manager: PM, logger: L, scorer: Option, ) -> Self where - CA::Target: 'static + chain::Access, + UL::Target: 'static + UtxoLookup, CF::Target: 'static + chain::Filter, CW::Target: 'static + chain::Watch<::Signer>, T::Target: 'static + BroadcasterInterface, diff --git a/lightning-net-tokio/src/lib.rs b/lightning-net-tokio/src/lib.rs index 16680faa..9b480fb0 100644 --- a/lightning-net-tokio/src/lib.rs +++ b/lightning-net-tokio/src/lib.rs @@ -33,12 +33,12 @@ //! type FeeEstimator = dyn lightning::chain::chaininterface::FeeEstimator + Send + Sync; //! type Logger = dyn lightning::util::logger::Logger + Send + Sync; //! type NodeSigner = dyn lightning::chain::keysinterface::NodeSigner + Send + Sync; -//! type ChainAccess = dyn lightning::chain::Access + Send + Sync; +//! type UtxoLookup = dyn lightning::routing::utxo::UtxoLookup + Send + Sync; //! type ChainFilter = dyn lightning::chain::Filter + Send + Sync; //! type DataPersister = dyn lightning::chain::chainmonitor::Persist + Send + Sync; //! type ChainMonitor = lightning::chain::chainmonitor::ChainMonitor, Arc, Arc, Arc, Arc>; //! type ChannelManager = Arc>; -//! type PeerManager = Arc>; +//! type PeerManager = Arc>; //! //! // Connect to node with pubkey their_node_id at addr: //! async fn connect_to_node(peer_manager: PeerManager, chain_monitor: Arc, channel_manager: ChannelManager, their_node_id: PublicKey, addr: SocketAddr) { diff --git a/lightning/src/chain/mod.rs b/lightning/src/chain/mod.rs index 0370c084..cd11bc33 100644 --- a/lightning/src/chain/mod.rs +++ b/lightning/src/chain/mod.rs @@ -12,7 +12,6 @@ use bitcoin::blockdata::block::{Block, BlockHeader}; use bitcoin::blockdata::constants::genesis_block; use bitcoin::blockdata::script::Script; -use bitcoin::blockdata::transaction::TxOut; use bitcoin::hash_types::{BlockHash, Txid}; use bitcoin::network::constants::Network; use bitcoin::secp256k1::PublicKey; @@ -60,26 +59,6 @@ impl BestBlock { pub fn height(&self) -> u32 { self.height } } -/// An error when accessing the chain via [`Access`]. -#[derive(Clone, Debug)] -pub enum AccessError { - /// The requested chain is unknown. - UnknownChain, - - /// The requested transaction doesn't exist or hasn't confirmed. - UnknownTx, -} - -/// The `Access` trait defines behavior for accessing chain data and state, such as blocks and -/// UTXOs. -pub trait Access { - /// Returns the transaction output of a funding transaction encoded by [`short_channel_id`]. - /// Returns an error if `genesis_hash` is for a different chain or if such a transaction output - /// is unknown. - /// - /// [`short_channel_id`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#definition-of-short_channel_id - fn get_utxo(&self, genesis_hash: &BlockHash, short_channel_id: u64) -> Result; -} /// The `Listen` trait is used to notify when blocks have been connected or disconnected from the /// chain. diff --git a/lightning/src/routing/gossip.rs b/lightning/src/routing/gossip.rs index a3331ab3..bbb8732f 100644 --- a/lightning/src/routing/gossip.rs +++ b/lightning/src/routing/gossip.rs @@ -19,14 +19,13 @@ use bitcoin::hashes::Hash; use bitcoin::blockdata::transaction::TxOut; use bitcoin::hash_types::BlockHash; -use crate::chain; -use crate::chain::Access; use crate::ln::chan_utils::make_funding_redeemscript_from_slices; use crate::ln::features::{ChannelFeatures, NodeFeatures, InitFeatures}; use crate::ln::msgs::{DecodeError, ErrorAction, Init, LightningError, RoutingMessageHandler, NetAddress, MAX_VALUE_MSAT}; use crate::ln::msgs::{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement, GossipTimestampFilter}; use crate::ln::msgs::{QueryChannelRange, ReplyChannelRange, QueryShortChannelIds, ReplyShortChannelIdsEnd}; use crate::ln::msgs; +use crate::routing::utxo::{UtxoLookup, UtxoLookupError}; use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, MaybeReadable}; use crate::util::logger::{Logger, Level}; use crate::util::events::{MessageSendEvent, MessageSendEventsProvider}; @@ -218,31 +217,30 @@ impl_writeable_tlv_based_enum_upgradable!(NetworkUpdate, /// This network graph is then used for routing payments. /// Provides interface to help with initial routing sync by /// serving historical announcements. -pub struct P2PGossipSync>, C: Deref, L: Deref> -where C::Target: chain::Access, L::Target: Logger +pub struct P2PGossipSync>, U: Deref, L: Deref> +where U::Target: UtxoLookup, L::Target: Logger { network_graph: G, - chain_access: Option, + utxo_lookup: Option, #[cfg(feature = "std")] full_syncs_requested: AtomicUsize, pending_events: Mutex>, logger: L, } -impl>, C: Deref, L: Deref> P2PGossipSync -where C::Target: chain::Access, L::Target: Logger +impl>, U: Deref, L: Deref> P2PGossipSync +where U::Target: UtxoLookup, L::Target: Logger { /// Creates a new tracker of the actual state of the network of channels and nodes, /// assuming an existing Network Graph. - /// Chain monitor is used to make sure announced channels exist on-chain, - /// channel data is correct, and that the announcement is signed with - /// channel owners' keys. - pub fn new(network_graph: G, chain_access: Option, logger: L) -> Self { + /// UTXO lookup is used to make sure announced channels exist on-chain, channel data is + /// correct, and the announcement is signed with channel owners' keys. + pub fn new(network_graph: G, utxo_lookup: Option, logger: L) -> Self { P2PGossipSync { network_graph, #[cfg(feature = "std")] full_syncs_requested: AtomicUsize::new(0), - chain_access, + utxo_lookup, pending_events: Mutex::new(vec![]), logger, } @@ -251,8 +249,8 @@ where C::Target: chain::Access, L::Target: Logger /// Adds a provider used to check new announcements. Does not affect /// existing announcements unless they are updated. /// Add, update or remove the provider would replace the current one. - pub fn add_chain_access(&mut self, chain_access: Option) { - self.chain_access = chain_access; + pub fn add_utxo_lookup(&mut self, utxo_lookup: Option) { + self.utxo_lookup = utxo_lookup; } /// Gets a reference to the underlying [`NetworkGraph`] which was provided in @@ -342,8 +340,8 @@ macro_rules! get_pubkey_from_node_id { } } -impl>, C: Deref, L: Deref> RoutingMessageHandler for P2PGossipSync -where C::Target: chain::Access, L::Target: Logger +impl>, U: Deref, L: Deref> RoutingMessageHandler for P2PGossipSync +where U::Target: UtxoLookup, L::Target: Logger { fn handle_node_announcement(&self, msg: &msgs::NodeAnnouncement) -> Result { self.network_graph.update_node_from_announcement(msg)?; @@ -353,7 +351,7 @@ where C::Target: chain::Access, L::Target: Logger } fn handle_channel_announcement(&self, msg: &msgs::ChannelAnnouncement) -> Result { - self.network_graph.update_channel_from_announcement(msg, &self.chain_access)?; + self.network_graph.update_channel_from_announcement(msg, &self.utxo_lookup)?; log_gossip!(self.logger, "Added channel_announcement for {}{}", msg.contents.short_channel_id, if !msg.contents.excess_data.is_empty() { " with excess uninterpreted data!" } else { "" }); Ok(msg.contents.excess_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY) } @@ -632,9 +630,9 @@ where C::Target: chain::Access, L::Target: Logger } } -impl>, C: Deref, L: Deref> MessageSendEventsProvider for P2PGossipSync +impl>, U: Deref, L: Deref> MessageSendEventsProvider for P2PGossipSync where - C::Target: chain::Access, + U::Target: UtxoLookup, L::Target: Logger, { fn get_and_clear_pending_msg_events(&self) -> Vec { @@ -1337,35 +1335,35 @@ impl NetworkGraph where L::Target: Logger { /// RoutingMessageHandler implementation to call it indirectly. This may be useful to accept /// routing messages from a source using a protocol other than the lightning P2P protocol. /// - /// If a `chain::Access` object is provided via `chain_access`, it will be called to verify + /// If a [`UtxoLookup`] object is provided via `utxo_lookup`, it will be called to verify /// the corresponding UTXO exists on chain and is correctly-formatted. - pub fn update_channel_from_announcement( - &self, msg: &msgs::ChannelAnnouncement, chain_access: &Option, + pub fn update_channel_from_announcement( + &self, msg: &msgs::ChannelAnnouncement, utxo_lookup: &Option, ) -> Result<(), LightningError> where - C::Target: chain::Access, + U::Target: UtxoLookup, { let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]); secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.node_signature_1, &get_pubkey_from_node_id!(msg.contents.node_id_1, "channel_announcement"), "channel_announcement"); secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.node_signature_2, &get_pubkey_from_node_id!(msg.contents.node_id_2, "channel_announcement"), "channel_announcement"); secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.bitcoin_signature_1, &get_pubkey_from_node_id!(msg.contents.bitcoin_key_1, "channel_announcement"), "channel_announcement"); secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.bitcoin_signature_2, &get_pubkey_from_node_id!(msg.contents.bitcoin_key_2, "channel_announcement"), "channel_announcement"); - self.update_channel_from_unsigned_announcement_intern(&msg.contents, Some(msg), chain_access) + self.update_channel_from_unsigned_announcement_intern(&msg.contents, Some(msg), utxo_lookup) } /// Store or update channel info from a channel announcement without verifying the associated /// signatures. Because we aren't given the associated signatures here we cannot relay the /// channel announcement to any of our peers. /// - /// If a `chain::Access` object is provided via `chain_access`, it will be called to verify + /// If a [`UtxoLookup`] object is provided via `utxo_lookup`, it will be called to verify /// the corresponding UTXO exists on chain and is correctly-formatted. - pub fn update_channel_from_unsigned_announcement( - &self, msg: &msgs::UnsignedChannelAnnouncement, chain_access: &Option + pub fn update_channel_from_unsigned_announcement( + &self, msg: &msgs::UnsignedChannelAnnouncement, utxo_lookup: &Option ) -> Result<(), LightningError> where - C::Target: chain::Access, + U::Target: UtxoLookup, { - self.update_channel_from_unsigned_announcement_intern(msg, None, chain_access) + self.update_channel_from_unsigned_announcement_intern(msg, None, utxo_lookup) } /// Update channel from partial announcement data received via rapid gossip sync @@ -1444,11 +1442,11 @@ impl NetworkGraph where L::Target: Logger { Ok(()) } - fn update_channel_from_unsigned_announcement_intern( - &self, msg: &msgs::UnsignedChannelAnnouncement, full_msg: Option<&msgs::ChannelAnnouncement>, chain_access: &Option + fn update_channel_from_unsigned_announcement_intern( + &self, msg: &msgs::UnsignedChannelAnnouncement, full_msg: Option<&msgs::ChannelAnnouncement>, utxo_lookup: &Option ) -> Result<(), LightningError> where - C::Target: chain::Access, + U::Target: UtxoLookup, { if msg.node_id_1 == msg.node_id_2 || msg.bitcoin_key_1 == msg.bitcoin_key_2 { return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError}); @@ -1476,7 +1474,7 @@ impl NetworkGraph where L::Target: Logger { action: ErrorAction::IgnoreDuplicateGossip }); } - } else if chain_access.is_none() { + } else if utxo_lookup.is_none() { // Similarly, if we can't check the chain right now anyway, ignore the // duplicate announcement without bothering to take the channels write lock. return Err(LightningError { @@ -1499,13 +1497,13 @@ impl NetworkGraph where L::Target: Logger { } } - let utxo_value = match &chain_access { + let utxo_value = match &utxo_lookup { &None => { // Tentatively accept, potentially exposing us to DoS attacks None }, - &Some(ref chain_access) => { - match chain_access.get_utxo(&msg.chain_hash, msg.short_channel_id) { + &Some(ref utxo_lookup) => { + match utxo_lookup.get_utxo(&msg.chain_hash, msg.short_channel_id) { Ok(TxOut { value, script_pubkey }) => { let expected_script = make_funding_redeemscript_from_slices(msg.bitcoin_key_1.as_slice(), msg.bitcoin_key_2.as_slice()).to_v0_p2wsh(); @@ -1516,10 +1514,10 @@ impl NetworkGraph where L::Target: Logger { //to the new HTLC max field in channel_update Some(value) }, - Err(chain::AccessError::UnknownChain) => { + Err(UtxoLookupError::UnknownChain) => { return Err(LightningError{err: format!("Channel announced on an unknown chain ({})", msg.chain_hash.encode().to_hex()), action: ErrorAction::IgnoreError}); }, - Err(chain::AccessError::UnknownTx) => { + Err(UtxoLookupError::UnknownTx) => { return Err(LightningError{err: "Channel announced without corresponding UTXO entry".to_owned(), action: ErrorAction::IgnoreError}); }, } @@ -1904,12 +1902,12 @@ impl ReadOnlyNetworkGraph<'_> { #[cfg(test)] mod tests { - use crate::chain; use crate::ln::channelmanager; use crate::ln::chan_utils::make_funding_redeemscript; #[cfg(feature = "std")] use crate::ln::features::InitFeatures; use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate, NodeAlias, MAX_EXCESS_BYTES_FOR_RELAY, NodeId, RoutingFees, ChannelUpdateInfo, ChannelInfo, NodeAnnouncementInfo, NodeInfo}; + use crate::routing::utxo::UtxoLookupError; use crate::ln::msgs::{RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement, UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate, ReplyChannelRange, QueryChannelRange, QueryShortChannelIds, MAX_VALUE_MSAT}; @@ -2141,7 +2139,7 @@ mod tests { // Test if an associated transaction were not on-chain (or not confirmed). let chain_source = test_utils::TestChainSource::new(Network::Testnet); - *chain_source.utxo_ret.lock().unwrap() = Err(chain::AccessError::UnknownTx); + *chain_source.utxo_ret.lock().unwrap() = Err(UtxoLookupError::UnknownTx); let network_graph = NetworkGraph::new(genesis_hash, &logger); gossip_sync = P2PGossipSync::new(&network_graph, Some(&chain_source), &logger); diff --git a/lightning/src/routing/mod.rs b/lightning/src/routing/mod.rs index 9bf09106..7fff8563 100644 --- a/lightning/src/routing/mod.rs +++ b/lightning/src/routing/mod.rs @@ -9,6 +9,7 @@ //! Structs and impls for receiving messages about the network and storing the topology live here. +pub mod utxo; pub mod gossip; pub mod router; pub mod scoring; diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 950384d9..a2c98212 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -3530,7 +3530,7 @@ mod tests { .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh(); *chain_monitor.utxo_ret.lock().unwrap() = Ok(TxOut { value: 15, script_pubkey: good_script.clone() }); - gossip_sync.add_chain_access(Some(chain_monitor)); + gossip_sync.add_utxo_lookup(Some(chain_monitor)); add_channel(&gossip_sync, &secp_ctx, &privkeys[0], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 333); update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate { diff --git a/lightning/src/routing/utxo.rs b/lightning/src/routing/utxo.rs new file mode 100644 index 00000000..1008e6a3 --- /dev/null +++ b/lightning/src/routing/utxo.rs @@ -0,0 +1,36 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +//! This module contains traits for LDK to access UTXOs to check gossip data is correct. +//! +//! When lightning nodes gossip channel information, they resist DoS attacks by checking that each +//! channel matches a UTXO on-chain, requiring at least some marginal on-chain transacting in +//! order to announce a channel. This module handles that checking. + +use bitcoin::{BlockHash, TxOut}; + +/// An error when accessing the chain via [`UtxoLookup`]. +#[derive(Clone, Debug)] +pub enum UtxoLookupError { + /// The requested chain is unknown. + UnknownChain, + + /// The requested transaction doesn't exist or hasn't confirmed. + UnknownTx, +} + +/// The `UtxoLookup` trait defines behavior for accessing on-chain UTXOs. +pub trait UtxoLookup { + /// Returns the transaction output of a funding transaction encoded by [`short_channel_id`]. + /// Returns an error if `genesis_hash` is for a different chain or if such a transaction output + /// is unknown. + /// + /// [`short_channel_id`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#definition-of-short_channel_id + fn get_utxo(&self, genesis_hash: &BlockHash, short_channel_id: u64) -> Result; +} diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index be09c8f0..77324231 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -22,8 +22,8 @@ use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures}; use crate::ln::{msgs, wire}; use crate::ln::msgs::LightningError; use crate::ln::script::ShutdownScript; -use crate::routing::gossip::NetworkGraph; -use crate::routing::gossip::NodeId; +use crate::routing::gossip::{NetworkGraph, NodeId}; +use crate::routing::utxo::{UtxoLookup, UtxoLookupError}; use crate::routing::router::{find_route, InFlightHtlcs, Route, RouteHop, RouteParameters, Router, ScorerAccountingForInFlightHtlcs}; use crate::routing::scoring::FixedPenaltyScorer; use crate::util::config::UserConfig; @@ -839,7 +839,7 @@ impl core::fmt::Debug for OnGetShutdownScriptpubkey { pub struct TestChainSource { pub genesis_hash: BlockHash, - pub utxo_ret: Mutex>, + pub utxo_ret: Mutex>, pub watched_txn: Mutex>, pub watched_outputs: Mutex>, } @@ -856,10 +856,10 @@ impl TestChainSource { } } -impl chain::Access for TestChainSource { - fn get_utxo(&self, genesis_hash: &BlockHash, _short_channel_id: u64) -> Result { +impl UtxoLookup for TestChainSource { + fn get_utxo(&self, genesis_hash: &BlockHash, _short_channel_id: u64) -> Result { if self.genesis_hash != *genesis_hash { - return Err(chain::AccessError::UnknownChain); + return Err(UtxoLookupError::UnknownChain); } self.utxo_ret.lock().unwrap().clone() -- 2.30.2