@@ -9,8 +9,14 @@ use bitcoin::hashes::hex::ToHex;
9
9
10
10
use crate :: ln:: chan_utils:: make_funding_redeemscript;
11
11
use crate :: ln:: msgs:: { self , LightningError , ErrorAction } ;
12
+ use crate :: routing:: gossip:: { NetworkGraph , NodeId } ;
13
+ use crate :: util:: logger:: Logger ;
12
14
use crate :: util:: ser:: Writeable ;
13
15
16
+ use crate :: prelude:: * ;
17
+
18
+ use alloc:: sync:: { Arc , Weak } ;
19
+ use crate :: sync:: Mutex ;
14
20
use core:: ops:: Deref ;
15
21
16
22
/// An error when accessing the chain via [`ChainAccess`].
@@ -23,6 +29,19 @@ pub enum ChainAccessError {
23
29
UnknownTx ,
24
30
}
25
31
32
+ /// The result of a [`ChainAccess::get_utxo`] call. A call may resolve either synchronously,
33
+ /// returning the `Sync` variant, or asynchronously, returning an [`AccessFuture`] in the `Async`
34
+ /// variant.
35
+ pub enum ChainAccessResult {
36
+ /// A result which was resolved synchronously. It either includes a [`TxOut`] for the output
37
+ /// requested or a [`ChainAccessError`].
38
+ Sync ( Result < TxOut , ChainAccessError > ) ,
39
+ /// A result which will be resolved asynchronously. It includes an [`AccessFuture`], a `clone`
40
+ /// of which you must keep locally and call [`AccessFuture::resolve`] on once the lookup
41
+ /// completes.
42
+ Async ( AccessFuture ) ,
43
+ }
44
+
26
45
/// The `ChainAccess` trait defines behavior for accessing chain data and state, such as blocks and
27
46
/// UTXOs.
28
47
pub trait ChainAccess {
@@ -31,19 +50,90 @@ pub trait ChainAccess {
31
50
/// is unknown.
32
51
///
33
52
/// [`short_channel_id`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#definition-of-short_channel_id
34
- fn get_utxo ( & self , genesis_hash : & BlockHash , short_channel_id : u64 ) -> Result < TxOut , ChainAccessError > ;
53
+ fn get_utxo ( & self , genesis_hash : & BlockHash , short_channel_id : u64 ) -> ChainAccessResult ;
54
+ }
55
+
56
+ enum ChannelAnnouncement {
57
+ Full ( msgs:: ChannelAnnouncement ) ,
58
+ Unsigned ( msgs:: UnsignedChannelAnnouncement ) ,
59
+ }
60
+
61
+ struct AccessMessages {
62
+ complete : Option < Result < TxOut , ChainAccessError > > ,
63
+ channel_announce : Option < ChannelAnnouncement > ,
64
+ }
65
+
66
+ #[ derive( Clone ) ]
67
+ pub struct AccessFuture {
68
+ state : Arc < Mutex < AccessMessages > > ,
69
+ }
70
+
71
+ /// A trivial implementation of [`ChainAccess`] which is used to call back into the network graph
72
+ /// once we have a concrete resolution of a request.
73
+ struct AccessResolver ( Result < TxOut , ChainAccessError > ) ;
74
+ impl ChainAccess for AccessResolver {
75
+ fn get_utxo ( & self , _genesis_hash : & BlockHash , _short_channel_id : u64 ) -> ChainAccessResult {
76
+ ChainAccessResult :: Sync ( self . 0 . clone ( ) )
77
+ }
78
+ }
79
+
80
+ impl AccessFuture {
81
+ /// Builds a new, empty, future for later resolution.
82
+ pub fn new ( ) -> Self {
83
+ Self { state : Arc :: new ( Mutex :: new ( AccessMessages {
84
+ complete : None ,
85
+ channel_announce : None ,
86
+ } ) ) }
87
+ }
88
+
89
+ /// Resolves this future against the given `graph` and with the given `result`.
90
+ pub fn resolve < L : Deref > ( & self , graph : & NetworkGraph < L > , result : Result < TxOut , ChainAccessError > )
91
+ where L :: Target : Logger {
92
+ let announcement = {
93
+ let mut async_messages = self . state . lock ( ) . unwrap ( ) ;
94
+
95
+ if async_messages. channel_announce . is_none ( ) {
96
+ // We raced returning to `check_channel_announcement` which hasn't updated
97
+ // `channel_announce` yet. That's okay, we can set the `complete` field which it will
98
+ // check once it gets control again.
99
+ async_messages. complete = Some ( result) ;
100
+ return ;
101
+ }
102
+
103
+ async_messages. channel_announce . take ( ) . unwrap ( )
104
+ } ;
105
+
106
+ // Now that we've updated our internal state, pass the pending messages back through the
107
+ // network graph with a different `ChainAccess` which will resolve immediately.
108
+ // Note that we ignore errors as we don't disconnect peers anyway, so there's nothing to do
109
+ // with them.
110
+ let resolver = AccessResolver ( result) ;
111
+ match announcement {
112
+ ChannelAnnouncement :: Full ( signed_msg) => {
113
+ let _ = graph. update_channel_from_announcement ( & signed_msg, & Some ( & resolver) ) ;
114
+ } ,
115
+ ChannelAnnouncement :: Unsigned ( msg) => {
116
+ let _ = graph. update_channel_from_unsigned_announcement ( & msg, & Some ( & resolver) ) ;
117
+ } ,
118
+ }
119
+ }
120
+ }
121
+
122
+ /// A set of messages which are pending UTXO lookups for processing.
123
+ pub ( super ) struct PendingChecks {
35
124
}
36
125
37
- pub ( crate ) fn check_channel_announcement < A : Deref > (
38
- chain_access : & Option < A > , msg : & msgs:: UnsignedChannelAnnouncement
39
- ) -> Result < Option < u64 > , msgs:: LightningError > where A :: Target : ChainAccess {
40
- match chain_access {
41
- & None => {
42
- // Tentatively accept, potentially exposing us to DoS attacks
43
- Ok ( None )
44
- } ,
45
- & Some ( ref chain_access) => {
46
- match chain_access. get_utxo ( & msg. chain_hash , msg. short_channel_id ) {
126
+ impl PendingChecks {
127
+ pub ( super ) fn new ( ) -> Self {
128
+ PendingChecks { }
129
+ }
130
+
131
+ pub ( super ) fn check_channel_announcement < A : Deref > ( & self ,
132
+ chain_access : & Option < A > , msg : & msgs:: UnsignedChannelAnnouncement ,
133
+ full_msg : Option < & msgs:: ChannelAnnouncement >
134
+ ) -> Result < Option < u64 > , msgs:: LightningError > where A :: Target : ChainAccess {
135
+ let handle_result = |res| {
136
+ match res {
47
137
Ok ( TxOut { value, script_pubkey } ) => {
48
138
let expected_script =
49
139
make_funding_redeemscript ( & msg. bitcoin_key_1 , & msg. bitcoin_key_2 ) . to_v0_p2wsh ( ) ;
@@ -70,6 +160,34 @@ pub(crate) fn check_channel_announcement<A: Deref>(
70
160
} )
71
161
} ,
72
162
}
163
+ } ;
164
+
165
+ match chain_access {
166
+ & None => {
167
+ // Tentatively accept, potentially exposing us to DoS attacks
168
+ Ok ( None )
169
+ } ,
170
+ & Some ( ref chain_access) => {
171
+ match chain_access. get_utxo ( & msg. chain_hash , msg. short_channel_id ) {
172
+ ChainAccessResult :: Sync ( res) => handle_result ( res) ,
173
+ ChainAccessResult :: Async ( future) => {
174
+ let mut async_messages = future. state . lock ( ) . unwrap ( ) ;
175
+ if let Some ( res) = async_messages. complete . take ( ) {
176
+ // In the unlikely event the future resolved before we managed to get it,
177
+ // handle the result in-line.
178
+ handle_result ( res)
179
+ } else {
180
+ async_messages. channel_announce = Some (
181
+ if let Some ( msg) = full_msg { ChannelAnnouncement :: Full ( msg. clone ( ) ) }
182
+ else { ChannelAnnouncement :: Unsigned ( msg. clone ( ) ) } ) ;
183
+ Err ( LightningError {
184
+ err : "Channel being checked async" . to_owned ( ) ,
185
+ action : ErrorAction :: IgnoreError
186
+ } )
187
+ }
188
+ } ,
189
+ }
190
+ }
73
191
}
74
192
}
75
193
}
0 commit comments