[Java] Expose the ProbabilisticScorer from the CMC correctly
[ldk-java] / src / main / java / org / ldk / batteries / ChannelManagerConstructor.java
1 package org.ldk.batteries;
2
3 import javax.annotation.Nullable;
4
5 import org.ldk.enums.Network;
6 import org.ldk.structs.*;
7
8 import java.io.IOException;
9 import java.util.HashSet;
10
11
12 /**
13  * A simple utility class which assists in constructing a fresh or deserializing from disk a ChannelManager and one or
14  * more ChannelMonitors.
15  *
16  * Also constructs a PeerManager and spawns a background thread to monitor for and notify you of relevant Events.
17  *
18  * Note that you must ensure you hold a reference to any constructed ChannelManagerConstructor objects to ensure you
19  * continue to receive events generated by the background thread which will be stopped if this object is garbage
20  * collected.
21  */
22 public class ChannelManagerConstructor {
23     /**
24      * An Exception that indicates the serialized data is invalid and has been corrupted on disk. You should attempt to
25      * restore from a backup if there is one which is known to be current. Otherwise, funds may have been lost.
26      */
27     public static class InvalidSerializedDataException extends Exception {
28         InvalidSerializedDataException(String reason) {
29             super(reason);
30         }
31     }
32
33     /**
34      * The ChannelManager either deserialized or newly-constructed.
35      */
36     public final ChannelManager channel_manager;
37     /**
38      * The latest block has the channel manager saw. If this is non-null it is a 32-byte block hash.
39      * You should sync the blockchain starting with the block that builds on this block.
40      */
41     public final byte[] channel_manager_latest_block_hash;
42     /**
43      * A list of ChannelMonitors and the last block they each saw. You should sync the blockchain on each individually
44      * starting with the block that builds on the hash given.
45      * After doing so (and syncing the blockchain on the channel manager as well), you should call chain_sync_completed()
46      * and then continue to normal application operation.
47      */
48     public final TwoTuple_BlockHashChannelMonitorZ[] channel_monitors;
49     /**
50      * A PeerManager which is constructed to pass messages and handle connections to peers.
51      *
52      * This is `null` until `chain_sync_completed` is called.
53      */
54     public PeerManager peer_manager = null;
55     /**
56      * A NioPeerHandler which manages a background thread to handle socket events and pass them to the peer_manager.
57          *
58          * This is `null` until `chain_sync_completed` is called.
59      */
60     public NioPeerHandler nio_peer_handler = null;
61
62     private final ChainMonitor chain_monitor;
63
64     /**
65      * The `NetworkGraph` deserialized from the byte given to the constructor when deserializing or the `NetworkGraph`
66      * given explicitly to the new-object constructor.
67      */
68     public final NetworkGraph net_graph;
69
70     /**
71      * A mutex holding the `ProbabilisticScorer` which was loaded on startup.
72      */
73     public final MultiThreadedLockableScore scorer;
74     /**
75      * We wrap the scorer in a MultiThreadedLockableScore which ultimately gates access to the scorer, however sometimes
76      * we want to expose underlying details of the scorer itself. Thus, we expose a safe version that takes the lock
77      * then returns a reference to this scorer.
78      */
79     private final ProbabilisticScorer prob_scorer;
80     private final Logger logger;
81     private final KeysManager keys_manager;
82
83     /**
84      * Exposes the `ProbabilisticScorer` wrapped inside a lock. Don't forget to `close` this lock when you're done with
85      * it so normal scoring operation can continue.
86      */
87     public class ScorerWrapper implements AutoCloseable {
88         private final Score lock;
89         public final ProbabilisticScorer prob_scorer;
90         private ScorerWrapper(Score lock, ProbabilisticScorer prob_scorer) {
91             this.lock = lock; this.prob_scorer = prob_scorer;
92         }
93         @Override public void close() throws Exception {
94             lock.destroy();
95         }
96     }
97     /**
98      * Gets the `ProbabilisticScorer` which backs the public lockable `scorer`. Don't forget to `close` the lock when
99      * you're done with it.
100      */
101     public ScorerWrapper get_locked_scorer() {
102         return new ScorerWrapper(this.scorer.as_LockableScore().lock(), this.prob_scorer);
103     }
104
105     /**
106      * Deserializes a channel manager and a set of channel monitors from the given serialized copies and interface implementations
107      *
108      * @param filter If provided, the outputs which were previously registered to be monitored for will be loaded into the filter.
109      *               Note that if the provided Watch is a ChainWatch and has an associated filter, the previously registered
110      *               outputs will be loaded when chain_sync_completed is called.
111      */
112     public ChannelManagerConstructor(byte[] channel_manager_serialized, byte[][] channel_monitors_serialized, UserConfig config,
113                                      KeysManager keys_manager, FeeEstimator fee_estimator, ChainMonitor chain_monitor,
114                                      @Nullable Filter filter, byte[] net_graph_serialized,
115                                      ProbabilisticScoringParameters scoring_params, byte[] probabilistic_scorer_bytes,
116                                      BroadcasterInterface tx_broadcaster, Logger logger) throws InvalidSerializedDataException {
117         this.keys_manager = keys_manager;
118         EntropySource entropy_source = keys_manager.as_EntropySource();
119
120         Result_NetworkGraphDecodeErrorZ graph_res = NetworkGraph.read(net_graph_serialized, logger);
121         if (!graph_res.is_ok()) {
122             throw new InvalidSerializedDataException("Serialized Network Graph was corrupt");
123         }
124         this.net_graph = ((Result_NetworkGraphDecodeErrorZ.Result_NetworkGraphDecodeErrorZ_OK)graph_res).res;
125         assert(scoring_params != null);
126         assert(probabilistic_scorer_bytes != null);
127         Result_ProbabilisticScorerDecodeErrorZ scorer_res = ProbabilisticScorer.read(probabilistic_scorer_bytes, scoring_params, net_graph, logger);
128         if (!scorer_res.is_ok()) {
129             throw new InvalidSerializedDataException("Serialized ProbabilisticScorer was corrupt");
130         }
131         this.prob_scorer = ((Result_ProbabilisticScorerDecodeErrorZ.Result_ProbabilisticScorerDecodeErrorZ_OK)scorer_res).res;
132         this.scorer = MultiThreadedLockableScore.of(this.prob_scorer.as_Score());
133         DefaultRouter router = DefaultRouter.of(this.net_graph, logger, entropy_source.get_secure_random_bytes(), scorer.as_LockableScore());
134
135         final ChannelMonitor[] monitors = new ChannelMonitor[channel_monitors_serialized.length];
136         this.channel_monitors = new TwoTuple_BlockHashChannelMonitorZ[monitors.length];
137         HashSet<OutPoint> monitor_funding_set = new HashSet();
138         for (int i = 0; i < monitors.length; i++) {
139             Result_C2Tuple_BlockHashChannelMonitorZDecodeErrorZ res = UtilMethods.C2Tuple_BlockHashChannelMonitorZ_read(channel_monitors_serialized[i], entropy_source, keys_manager.as_SignerProvider());
140             if (res instanceof Result_C2Tuple_BlockHashChannelMonitorZDecodeErrorZ.Result_C2Tuple_BlockHashChannelMonitorZDecodeErrorZ_Err) {
141                 throw new InvalidSerializedDataException("Serialized ChannelMonitor was corrupt");
142             }
143             byte[] block_hash = ((Result_C2Tuple_BlockHashChannelMonitorZDecodeErrorZ.Result_C2Tuple_BlockHashChannelMonitorZDecodeErrorZ_OK)res).res.get_a();
144             monitors[i] = ((Result_C2Tuple_BlockHashChannelMonitorZDecodeErrorZ.Result_C2Tuple_BlockHashChannelMonitorZDecodeErrorZ_OK) res).res.get_b();
145             this.channel_monitors[i] = TwoTuple_BlockHashChannelMonitorZ.of(block_hash, monitors[i]);
146             if (!monitor_funding_set.add(monitors[i].get_funding_txo().get_a()))
147                 throw new InvalidSerializedDataException("Set of ChannelMonitors contained duplicates (ie the same funding_txo was set on multiple monitors)");
148         }
149         Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ res =
150                 UtilMethods.C2Tuple_BlockHashChannelManagerZ_read(channel_manager_serialized, keys_manager.as_EntropySource(),
151                         keys_manager.as_NodeSigner(), keys_manager.as_SignerProvider(), fee_estimator, chain_monitor.as_Watch(),
152                         tx_broadcaster, router.as_Router(), logger, config, monitors);
153         if (!res.is_ok()) {
154             throw new InvalidSerializedDataException("Serialized ChannelManager was corrupt");
155         }
156         this.channel_manager = ((Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ.Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ_OK)res).res.get_b();
157         this.channel_manager_latest_block_hash = ((Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ.Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ_OK)res).res.get_a();
158         this.chain_monitor = chain_monitor;
159         this.logger = logger;
160         if (filter != null) {
161             for (ChannelMonitor monitor : monitors) {
162                 monitor.load_outputs_to_watch(filter);
163             }
164         }
165     }
166
167     /**
168      * Constructs a channel manager from the given interface implementations
169      */
170     public ChannelManagerConstructor(Network network, UserConfig config, byte[] current_blockchain_tip_hash, int current_blockchain_tip_height,
171                                      KeysManager keys_manager, FeeEstimator fee_estimator, ChainMonitor chain_monitor,
172                                      NetworkGraph net_graph, ProbabilisticScoringParameters scoring_params,
173                                      BroadcasterInterface tx_broadcaster, Logger logger) {
174         this.keys_manager = keys_manager;
175         EntropySource entropy_source = keys_manager.as_EntropySource();
176
177         this.net_graph = net_graph;
178         assert(scoring_params != null);
179         this.prob_scorer = ProbabilisticScorer.of(scoring_params, net_graph, logger);
180         this.scorer = MultiThreadedLockableScore.of(this.prob_scorer.as_Score());
181         DefaultRouter router = DefaultRouter.of(this.net_graph, logger, entropy_source.get_secure_random_bytes(), scorer.as_LockableScore());
182
183         channel_monitors = new TwoTuple_BlockHashChannelMonitorZ[0];
184         channel_manager_latest_block_hash = null;
185         this.chain_monitor = chain_monitor;
186         BestBlock block = BestBlock.of(current_blockchain_tip_hash, current_blockchain_tip_height);
187         ChainParameters params = ChainParameters.of(network, block);
188         channel_manager = ChannelManager.of(fee_estimator, chain_monitor.as_Watch(), tx_broadcaster, router.as_Router(), logger,
189             keys_manager.as_EntropySource(), keys_manager.as_NodeSigner(), keys_manager.as_SignerProvider(), config, params);
190         this.logger = logger;
191     }
192
193     /**
194      * Abstract interface which should handle Events and persist the ChannelManager. When you call chain_sync_completed
195      * a background thread is started which will automatically call these methods for you when events occur.
196      */
197     public interface EventHandler {
198         void handle_event(Event events);
199         void persist_manager(byte[] channel_manager_bytes);
200         void persist_network_graph(byte[] network_graph);
201         void persist_scorer(byte[] scorer_bytes);
202     }
203
204     BackgroundProcessor background_processor = null;
205
206     /**
207      * Utility which adds all of the deserialized ChannelMonitors to the chain watch so that further updates from the
208      * ChannelManager are processed as normal.
209      *
210      * This also spawns a background thread which will call the appropriate methods on the provided
211      * EventHandler as required.
212      *
213      * @param use_p2p_graph_sync determines if we will sync the network graph from peers over the standard (but
214      *                           inefficient) lightning P2P protocol. Note that doing so currently requires trusting
215      *                           peers as no DoS mechanism is enforced to ensure we don't accept bogus gossip.
216      *                           Alternatively, you may sync the net_graph exposed in this object via Rapid Gossip Sync.
217      */
218     public void chain_sync_completed(EventHandler event_handler, boolean use_p2p_graph_sync) {
219         if (background_processor != null) { return; }
220         for (TwoTuple_BlockHashChannelMonitorZ monitor: channel_monitors) {
221             this.chain_monitor.as_Watch().watch_channel(monitor.get_b().get_funding_txo().get_a(), monitor.get_b());
222         }
223         org.ldk.structs.EventHandler ldk_handler = org.ldk.structs.EventHandler.new_impl(event_handler::handle_event);
224
225         final IgnoringMessageHandler ignoring_handler = IgnoringMessageHandler.of();
226         P2PGossipSync graph_msg_handler = P2PGossipSync.of(net_graph, Option_UtxoLookupZ.none(), logger);
227         this.peer_manager = PeerManager.of(channel_manager.as_ChannelMessageHandler(),
228                 ignoring_handler.as_RoutingMessageHandler(), ignoring_handler.as_OnionMessageHandler(),
229                 (int)(System.currentTimeMillis() / 1000), this.keys_manager.as_EntropySource().get_secure_random_bytes(),
230                 logger, ignoring_handler.as_CustomMessageHandler(), keys_manager.as_NodeSigner());
231
232         try {
233             this.nio_peer_handler = new NioPeerHandler(peer_manager);
234         } catch (IOException e) {
235             throw new IllegalStateException("We should never fail to construct nio objects unless we're on a platform that cannot run LDK.");
236         }
237
238         GossipSync gossip_sync;
239         if (use_p2p_graph_sync)
240             gossip_sync = GossipSync.none();
241         else
242             gossip_sync = GossipSync.p2_p(graph_msg_handler);
243
244         Option_WriteableScoreZ writeable_score = Option_WriteableScoreZ.some(scorer.as_WriteableScore());
245
246         background_processor = BackgroundProcessor.start(Persister.new_impl(new Persister.PersisterInterface() {
247             @Override
248             public Result_NoneErrorZ persist_manager(ChannelManager channel_manager) {
249                 event_handler.persist_manager(channel_manager.write());
250                 return Result_NoneErrorZ.ok();
251             }
252
253             @Override
254             public Result_NoneErrorZ persist_graph(NetworkGraph network_graph) {
255                 event_handler.persist_network_graph(network_graph.write());
256                 return Result_NoneErrorZ.ok();
257             }
258
259             @Override
260             public Result_NoneErrorZ persist_scorer(WriteableScore scorer) {
261                 event_handler.persist_scorer(scorer.write());
262                 return Result_NoneErrorZ.ok();
263             }
264         }), ldk_handler, this.chain_monitor, this.channel_manager, gossip_sync, peer_manager, this.logger, writeable_score);
265     }
266
267     /**
268      * Interrupt the background thread, stopping the background handling of events.
269      */
270     public void interrupt() {
271         if (this.nio_peer_handler != null)
272             this.nio_peer_handler.interrupt();
273         this.background_processor.stop();
274     }
275 }