diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
index 69f590d68..c8c02bbc7 100644
--- a/docs/.vuepress/config.js
+++ b/docs/.vuepress/config.js
@@ -92,8 +92,19 @@ const tutorialSidebar = [
collapsable: false,
children: [
'/tutorials/getting-started',
- '/tutorials/build_a_node_in_java',
- '/tutorials/build_a_node_in_rust'
+ {
+ title: 'Building a node with LDK',
+ collapsable: false,
+ children: [
+ ['/tutorials/building-a-node-with-ldk/introduction', 'Introduction'],
+ ['/tutorials/building-a-node-with-ldk/setting-up-a-channel-manager', 'Setting up a Channel Manager'],
+ ['/tutorials/building-a-node-with-ldk/handling-events', 'Handling Events'],
+ ['/tutorials/building-a-node-with-ldk/setting-up-a-peer-manager', 'Setting up a Peer Manager'],
+ ['/tutorials/building-a-node-with-ldk/connect-to-peers', 'Connect to Peers'],
+ ['/tutorials/building-a-node-with-ldk/opening-a-channel', 'Opening a Channel'],
+ ['/tutorials/building-a-node-with-ldk/sending-payments', 'Sending Payments']
+ ]
+ },
],
}
]
diff --git a/docs/tutorials/building-a-node-with-ldk/connect-to-peers.md b/docs/tutorials/building-a-node-with-ldk/connect-to-peers.md
new file mode 100644
index 000000000..402ed1113
--- /dev/null
+++ b/docs/tutorials/building-a-node-with-ldk/connect-to-peers.md
@@ -0,0 +1,107 @@
+# Connect to Peers
+
+In this section you'll learn how to join the lightning network.
+
+Firstly we need to have the ability to do high performance I/O operations. LDK provides default implementations for initializing all of your networking needs. If you are using Rust, you can use our simple socket handling library `lightning_net_tokio`. In Kotlin/Java you can use the `NioPeerHandler` which uses Java's NIO I/O interface.
+
+**What it's used for**: making peer connections, facilitating peer data to and from LDK
+
+
+
+
+```rust
+use lightning_net_tokio; // use LDK's sample networking module
+
+let listen_port = 9735;
+let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", listen_port))
+ .await.unwrap()
+loop {
+ let tcp_stream = listener.accept().await.unwrap().0;
+ tokio::spawn(async move {
+ // Use LDK's supplied networking battery to facilitate inbound
+ // connections.
+ lightning_net_tokio::setup_inbound(
+ &peer_manager,
+ tcp_stream.into_std().unwrap(),
+ )
+ .await;
+ });
+}
+```
+
+
+
+
+
+```java
+val nioPeerHandler = channelManagerConstructor.nio_peer_handler
+val port = 9777
+nioPeerHandler.bind_listener(InetSocketAddress("127.0.0.1", port))
+```
+
+
+
+
+
+Connections to other peers are established with `PeerManager`. You'll need to know the pubkey and address of another node that you want as a peer. Once the connection is established and the handshake is complete, `PeerManager` will show the peer's pubkey in its list of peers.
+
+
+
+
+```rust
+match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, address).await {
+ Some(connection_closed_future) => {
+ let mut connection_closed_future = Box::pin(connection_closed_future);
+ loop {
+ // Make sure the connection is still established.
+ match futures::poll!(&mut connection_closed_future) {
+ std::task::Poll::Ready(_) => {
+ panic!("ERROR: Peer disconnected before handshake completed");
+ }
+ std::task::Poll::Pending => {}
+ }
+
+ // Wait for the handshake to complete.
+ match peer_manager.get_peer_node_ids().iter().find(|id| **id == pubkey) {
+ Some(_) => break,
+ None => tokio::time::sleep(std::time::Duration::from_millis(10)).await,
+ }
+ }
+ }
+ None => panic!("ERROR: Failed to connect to peer"),
+}
+```
+
+
+
+
+
+```java
+try {
+ // Connect and wait for the handshake to complete.
+ val address: SocketAddress = InetSocketAddress(hostname, port)
+ nioPeerHandler.connect(pubkeyHex.toByteArray(), address, 5555)
+
+ // The peer's pubkey will be present in the list of peer ids.
+ val peerManager: PeerManager = channelManagerConstructor.peer_manager
+ val peerNodeIds = peerManager._peer_node_ids
+
+ } catch (e: IOException) {
+ // Handle failure when connecting to a peer.
+}
+```
+
+
+
+
+**Dependencies:** `PeerManager`
+
+**References:** [Rust `lightning-net-tokio` docs](https://docs.rs/lightning-net-tokio/*/lightning_net_tokio/), [Rust `PeerManager` docs](https://docs.rs/lightning/*/lightning/ln/peer_handler/struct.PeerManager.html), [Java `NioPeerHandler` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/batteries/NioPeerHandler.java),
+[Java `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/PeerManager.java),
+
+
+
+
+
+
+
diff --git a/docs/tutorials/building-a-node-with-ldk/handling-events.md b/docs/tutorials/building-a-node-with-ldk/handling-events.md
new file mode 100644
index 000000000..44cd0fa25
--- /dev/null
+++ b/docs/tutorials/building-a-node-with-ldk/handling-events.md
@@ -0,0 +1,47 @@
+# Handling Events
+
+LDK requires that you handle many different events throughout your app's life cycle. You can learn more by reading about our event-driven [architecture](/overview/architecture.md).
+
+To start handling events in your application, run:
+
+
+
+
+ ```rust
+ use lightning::util::events::{Event};
+
+ // In the event handler passed to BackgroundProcessor::start
+ match event {
+ Event::PaymentSent { payment_preimage } => {
+ // Handle successful payment
+ }
+ Event::PaymentFailed { payment_hash, rejected_by_dest } => {
+ // Handle failed payment
+ }
+ Event::FundingGenerationReady { .. } =>
+}
+ ```
+
+
+
+
+ ```java
+ import org.ldk.structs.Event
+
+ if (event is Event.PaymentSent) {
+ // Handle successful payment
+ }
+
+ if (event is Event.PaymentFailed) {
+ // Handle failed payment
+ }
+
+ if (event is Event.FundingGenerationReady) {
+ // Create a funding tx to be broadcast
+ }
+ ```
+
+
+
+
+References: [Rust `Event` docs](https://docs.rs/lightning/0.0.114/lightning/util/events/enum.Event.html), [Java `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Event.java)
\ No newline at end of file
diff --git a/docs/tutorials/building-a-node-with-ldk/introduction.md b/docs/tutorials/building-a-node-with-ldk/introduction.md
new file mode 100644
index 000000000..a355f1c53
--- /dev/null
+++ b/docs/tutorials/building-a-node-with-ldk/introduction.md
@@ -0,0 +1,35 @@
+# Building a Node with LDK
+
+## Learn how to build a basic LDK node from scratch
+
+::: tip Note
+For an integrated example of an LDK node in Rust, see the [Sample Node](https://github.com/lightningdevkit/ldk-sample)
+:::
+
+The following tutorials will show you how to build the simplest lightning node using LDK, that fufills the following tasks:
+
+1. **Connecting to Peers**
+2. **Open Channels**
+3. **Send Payments**
+4. **Receive Payments**
+5. **Close Channels**
+
+### Foundational Components
+
+Let's start by looking at the core components we'll need to make this node work for the tasks we outlined above.
+
+1. A [`PeerManager`](https://docs.rs/lightning/*/lightning/ln/peer_handler/struct.PeerManager.html), for establishing TCP/IP connections to other nodes on the lightning network.
+2. A [`ChannelManager`](https://docs.rs/lightning/*/lightning/ln/channelmanager/struct.ChannelManager.html), to open and close channels.
+3. Payments & Routing, ability to create and pay invoices.
+
+To make the above work we also need to setup a series of supporting modules, including:
+1. A [`FeeEstimator`](https://docs.rs/lightning/*/lightning/chain/chaininterface/trait.FeeEstimator.html)
+2. A [`Logger`](https://docs.rs/lightning/*/lightning/util/logger/index.html)
+3. A Transaction [`Broadcaster`](https://docs.rs/lightning/*/lightning/chain/chaininterface/trait.BroadcasterInterface.html)
+4. A [`NetworkGraph`](https://docs.rs/lightning/*/lightning/routing/gossip/struct.NetworkGraph.html)
+5. A [`Persister`](https://docs.rs/lightning/*/lightning/util/persist/trait.Persister.html)
+6. An [`EventHandler`](https://docs.rs/lightning/*/lightning/util/events/trait.EventHandler.html)
+7. A Transaction [`Filter`](https://docs.rs/lightning/*/lightning/chain/trait.Filter.html)
+8. A [`ChainMonitor`](https://docs.rs/lightning/*/lightning/chain/chainmonitor/index.html)
+9. A [`KeysManager`](https://docs.rs/lightning/*/lightning/chain/keysinterface/struct.KeysManager.html)
+10. A [`Scorer`](https://docs.rs/lightning/*/lightning/routing/scoring/index.html)
diff --git a/docs/tutorials/building-a-node-with-ldk/opening-a-channel.md b/docs/tutorials/building-a-node-with-ldk/opening-a-channel.md
new file mode 100644
index 000000000..ca3db0a8b
--- /dev/null
+++ b/docs/tutorials/building-a-node-with-ldk/opening-a-channel.md
@@ -0,0 +1,223 @@
+# Opening a Channel
+
+Channels are the basic building blocks of the Lightning Network. With channels, you can transact not only with your immediate peers but with others on the network. Let's explore how to open a channel with LDK.
+
+Now that you have a peer, you can open a channel with them using `ChannelManager`. You'll need the peer's pubkey as before along with:
+
+- the amount in sats to use when funding the channel,
+- any msats to push to your peer,
+- an id which is given back in the `FundingGenerationReady` event,
+- an optional `UserConfig` for overriding `ChannelManager` defaults
+
+Channels can be announced to the network or can remain private, which is controlled via `UserConfig::announced_channel`.
+
+
+
+
+```rust
+let amount = 10_000;
+let push_msat = 1_000;
+let user_id = 42;
+let config = UserConfig {
+ channel_options: ChannelConfig { announced_channel: true, ..Default::default() },
+ ..Default::default()
+};
+match channel_manager.create_channel(pubkey, amount, push_msat, user_id, Some(config)) {
+ Ok(_) => println!("EVENT: initiated channel with peer {}", pubkey),
+ Err(e) => panic!("ERROR: failed to open channel: {:?}", e),
+}
+```
+
+
+
+
+
+```kotlin
+val amount = 100_000L
+val pushMsat = 1_000L
+val userId = 42L
+
+// public aka announced channel
+val userConfig = UserConfig.with_default()
+
+val channelHandshakeConfig = ChannelHandshakeConfig.with_default()
+channelHandshakeConfig._announced_channel = true
+
+userConfig._channel_handshake_config = channelHandshakeConfig
+
+val createChannelResult = channelManager.create_channel(
+ pubKey.toByteArray(), amount, pushMsat, userId, userConfig
+)
+```
+
+
+
+
+# FundingGenerationReady Event Handling
+
+At this point, an outbound channel has been initiated with your peer and it will appear in `ChannelManager::list_channels`. However, the channel is not yet funded. Once your peer accepts the channel, you will be notified with a `FundingGenerationReady` event. It's then your responsibility to construct the funding transaction and pass it to ChannelManager, which will broadcast it once it receives your channel counterparty's signature.
+
+::: tip Note
+
+Remember that the funding transaction must only spend SegWit inputs.
+
+:::
+
+
+
+
+```rust
+// In the event handler passed to BackgroundProcessor::start
+match event {
+ Event::FundingGenerationReady {
+ temporary_channel_id,
+ channel_value_satoshis,
+ output_script,
+ user_channel_id,
+ } => {
+ // This is the same channel created earler.
+ assert_eq!(event.user_channel_id, 42);
+
+ // Construct the raw transaction with one output, that is paid the amount of the
+ // channel.
+ let network = bitcoin_bech32::constants::Network::Testnet;
+ let address = WitnessProgram::from_scriptpubkey(&output_script[..], network)
+ .unwrap().to_address;
+ let mut outputs = vec![HashMap::with_capacity(1)];
+ outputs[0].insert(address, channel_value_satoshis as f64 / 100_000_000.0);
+ let raw_tx = bitcoind_client.create_raw_transaction(outputs).await;
+
+ // Have your wallet put the inputs into the transaction such that the output is
+ // satisfied.
+ let funded_tx = bitcoind_client.fund_raw_transaction(raw_tx).await;
+ assert!(funded_tx.changepos == 0 || funded_tx.changepos == 1);
+
+ // Sign the funding transaction and give it to ChannelManager to broadcast.
+ let signed_tx = bitcoind_client.sign_raw_transaction_with_wallet(funded_tx.hex).await;
+ assert_eq!(signed_tx.complete, true);
+ let final_tx: Transaction =
+ encode::deserialize(&hex_utils::to_vec(&signed_tx.hex).unwrap()).unwrap();
+ channel_manager.funding_transaction_generated(&temporary_channel_id, final_tx).unwrap();
+ }
+ // ...
+}
+```
+
+
+
+
+
+```java
+// After the peer responds with an `accept_channel` message, an
+// Event.FundingGenerationReady event will be generated.
+
+if (event is Event.FundingGenerationReady) {
+ val funding_spk = event.output_script
+
+ if (funding_spk.size == 34 && funding_spk[0].toInt() == 0 && funding_spk[1].toInt() == 32) {
+ // Generate the funding transaction for the channel based on the channel amount
+ // The following uses BDK (Bitcoin Dev Kit) for on-chain logic
+ val rawTx = buildFundingTx(event.channel_value_satoshis, event.output_script)
+
+ channelManager.funding_transaction_generated(
+ event.temporary_channel_id,
+ event.counterparty_node_id,
+ rawTx
+ )
+ }
+ }
+
+fun buildFundingTx(value: Long, script: ByteArray): Transaction {
+ val scriptListUByte: List = script.toUByteArray().asList()
+ val outputScript = Script(scriptListUByte)
+ val (psbt, _) = TxBuilder()
+ .addRecipient(outputScript, value.toULong())
+ .feeRate(4.0F)
+ .finish(onchainWallet)
+ sign(psbt)
+ val rawTx = psbt.extractTx().serialize().toUByteArray().toByteArray()
+ return psbt.extractTx()
+}
+```
+
+
+
+
+
+**References:** [Rust `FundingGenerationReady` docs](https://docs.rs/lightning/*/lightning/util/events/enum.Event.html#variant.FundingGenerationReady), [Java `FundingGenerationReady` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Event.java#L95)
+# Broadcasting the Funding Transaction
+
+After crafting the funding transaction you'll need to send it to the Bitcoin network where it will hopefully be mined and added to the blockchain. You'll need to watch this transaction and wait for a minimum of 6 confirmations before the channel is ready to use.
+
+
+
+
+```rust
+// Using BDK (Bitcoin Dev Kit) to broadcast a transaction via the esplora client
+impl BroadcasterInterface for YourTxBroadcaster {
+ fn broadcast_transaction(&self, tx: &Transaction) {
+ let locked_runtime = self.tokio_runtime.read().unwrap();
+ if locked_runtime.as_ref().is_none() {
+ log_error!(self.logger, "Failed to broadcast transaction: No runtime.");
+ return;
+ }
+
+ let res = tokio::task::block_in_place(move || {
+ locked_runtime
+ .as_ref()
+ .unwrap()
+ .block_on(async move { self.blockchain.broadcast(tx).await })
+ });
+
+ match res {
+ Ok(_) => {}
+ Err(err) => {
+ log_error!(self.logger, "Failed to broadcast transaction: {}", err);
+ }
+ }
+ }
+}
+
+```
+
+
+
+
+
+```java
+
+// Using BDK (Bitcoin Dev Kit) to broadcast a transaction via the esplora client
+object YourTxBroadcaster : BroadcasterInterface.BroadcasterInterfaceInterface {
+ override fun broadcast_transaction(tx: ByteArray?) {
+ val esploraURL = "esploraUrl"
+ val blockchainConfig = BlockchainConfig.Esplora(EsploraConfig(esploraURL, null, 5u, 20u, null))
+ val blockchain = Blockchain(blockchainConfig)
+
+ val uByteArray = UByteArray(tx.size) { tx[it].toUByte() }
+ val transaction = Transaction(uByteArray.toList())
+
+ tx?.let {
+ CoroutineScope(Dispatchers.IO).launch {
+ blockchain.broadcast(transaction)
+ }
+ } ?: throw(IllegalStateException("Broadcaster attempted to broadcast a null transaction"))
+
+ }
+}
+
+```
+
+
+
+
+::: tip Keep LDK in sync
+
+Remember if you are restarting and have open channels then you should [let LDK know about the latest channel state.](./setting-up-a-channel-manager/#sync-channelmonitors-and-channelmanager-to-chain-tip)
+
+:::
+
+
+
+
+
+
diff --git a/docs/tutorials/building-a-node-with-ldk/sending-payments.md b/docs/tutorials/building-a-node-with-ldk/sending-payments.md
new file mode 100644
index 000000000..f12d8544a
--- /dev/null
+++ b/docs/tutorials/building-a-node-with-ldk/sending-payments.md
@@ -0,0 +1,169 @@
+# Sending Payments
+
+Lightning payments are used to pay invoices, which are typically encoded as a
+string in accordance with BOLT 11. After parsing the invoice, you'll need to
+find a route from your node to the recipient and then make the payment using
+`ChannelManager`.
+
+
+
+
+```rust
+// Parse the invoice.
+let invoice = Invoice::from_str(encoded_invoice)
+ .expect("ERROR: failed to parse invoice");
+
+let amt_pico_btc = invoice.amount_pico_btc()
+ .expect("ERROR: invalid invoice: must contain amount to pay");
+let amt_msat = amt_pico_btc / 10;
+let payer_pubkey = channel_manager.get_our_node_id();
+let network_graph = router.network_graph.read().unwrap();
+let payee_pubkey = invoice.recover_payee_pub_key();
+let payee_features = invoice.features().cloned();
+let first_hops = channel_manager.list_usable_channels();
+let last_hops = invoice.route_hints();
+let final_cltv = invoice.min_final_cltv_expiry() as u32;
+
+// Find a route and send the payment.
+let route = router::get_route(
+ &payer_pubkey, &network_graph, &payee_pubkey, payee_features,
+ Some(&first_hops.iter().collect::>()), &last_hops,
+ amt_msat, final_cltv, logger.clone(),
+).expect("ERROR: failed to find route");
+
+let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
+let payment_secret = invoice.payment_secret().cloned();
+
+channel_manager.send_payment(&route, payment_hash, &payment_secret)
+ .expect("ERROR: failed to send payment");
+```
+
+
+
+
+```java
+String invoice_str = // get an invoice from the payee
+Result_InvoiceNoneZ parsed_invoice = Invoice.from_str(invoice_str);
+
+if (parsed_invoice instanceof Result_InvoiceNoneZ.Result_InvoiceNoneZ_OK) {
+ Invoice invoice = ((Result_InvoiceNoneZ.Result_InvoiceNoneZ_OK) parsed_invoice).res;
+ long amt_msat = 0;
+ if (invoice.amount_pico_btc() instanceof Option_u64Z.Some) {
+ amt_msat = ((Option_u64Z.Some)invoice.amount_pico_btc()).some / 10;
+ }
+ if (amt_msat == 0) {
+ //
+ }
+
+ Route route;
+ try (LockedNetworkGraph netgraph = router.read_locked_graph()) {
+ NetworkGraph graph = netgraph.graph();
+ Result_RouteLightningErrorZ route_res = UtilMethods.get_route(
+ channel_manager.get_our_node_id(),
+ graph, invoice.recover_payee_pub_key(), invoice.features(),
+ channel_manager.list_usable_channels(), invoice.route_hints(),
+ amt_msat, invoice.min_final_cltv_expiry(), logger);
+ assert route_res instanceof Result_RouteLightningErrorZ.Result_RouteLightningErrorZ_OK;
+ route = ((Result_RouteLightningErrorZ.Result_RouteLightningErrorZ_OK) route_res).res;
+ }
+
+ Result_NonePaymentSendFailureZ payment_res = channel_manager.send_payment(
+ route, invoice.payment_hash(), invoice.payment_secret());
+ assert payment_res instanceof Result_NonePaymentSendFailureZ.Result_NonePaymentSendFailureZ_OK;
+}
+```
+
+
+
+
+```java
+// Get an invoice from the recipient/payee
+val parsedInvoice = Invoice.from_str(recipientInvoice)
+if (!parsedInvoice.is_ok) {
+ // Unable to parse invoice
+}
+
+val invoice = (parsedInvoice as Result_InvoiceParseOrSemanticErrorZ.Result_InvoiceParseOrSemanticErrorZ_OK).res
+
+var amountSats: Long = 0
+if (invoice.amount_milli_satoshis() is Option_u64Z.Some) {
+ amountSats = (invoice.amount_milli_satoshis() as Option_u64Z.Some).some * 1000
+}
+
+if (amountSats == 0L) {
+ // Handle a zero-value invoice
+}
+
+val res = channelManagerConstructor.payer.pay_invoice(invoice)
+
+
+
+if (parsed_invoice instanceof Result_InvoiceNoneZ.Result_InvoiceNoneZ_OK) {
+ Invoice invoice = ((Result_InvoiceNoneZ.Result_InvoiceNoneZ_OK) parsed_invoice).res;
+ long amt_msat = 0;
+ if (invoice.amount_pico_btc() instanceof Option_u64Z.Some) {
+ amt_msat = ((Option_u64Z.Some)invoice.amount_pico_btc()).some / 10;
+ }
+ if (amt_msat == 0) {
+ //
+ }
+
+ Route route;
+ try (LockedNetworkGraph netgraph = router.read_locked_graph()) {
+ NetworkGraph graph = netgraph.graph();
+ Result_RouteLightningErrorZ route_res = UtilMethods.get_route(
+ channel_manager.get_our_node_id(),
+ graph, invoice.recover_payee_pub_key(), invoice.features(),
+ channel_manager.list_usable_channels(), invoice.route_hints(),
+ amt_msat, invoice.min_final_cltv_expiry(), logger);
+ assert route_res instanceof Result_RouteLightningErrorZ.Result_RouteLightningErrorZ_OK;
+ route = ((Result_RouteLightningErrorZ.Result_RouteLightningErrorZ_OK) route_res).res;
+ }
+
+ Result_NonePaymentSendFailureZ payment_res = channel_manager.send_payment(
+ route, invoice.payment_hash(), invoice.payment_secret());
+ assert payment_res instanceof Result_NonePaymentSendFailureZ.Result_NonePaymentSendFailureZ_OK;
+}
+```
+
+
+
+
+An event is generated once a payment has completed. Successful payments result
+in a `PaymentSent` event with the preimage of the payment hash. Be sure to look
+out for a `PaymentFailed` event, if the payment fails for some reason, and act
+accordingly.
+
+
+
+
+```rust
+// In the event handler passed to BackgroundProcessor::start
+match event {
+ Event::PaymentSent { payment_preimage } => {
+ // Handle successful payment
+ }
+ Event::PaymentFailed { payment_hash, rejected_by_dest } => {
+ // Handle failed payment
+ }
+ // ...
+}
+```
+
+
+
+
+```java
+// In the `handle_event` method of ChannelManagerPersister implementation
+else if (e instanceof Event.PaymentSent) {
+ // Handle successful payment
+ Event.PaymentSent event = ((Event.PaymentSent) e);
+}
+else if (e instanceof Event.PaymentFailed) {
+ // Handle failed payment
+ Event.PaymentFailed event = ((Event.PaymentFailed) e);
+}
+```
+
+
+
diff --git a/docs/tutorials/building-a-node-with-ldk/setting-up-a-channel-manager.md b/docs/tutorials/building-a-node-with-ldk/setting-up-a-channel-manager.md
new file mode 100644
index 000000000..db3374ebe
--- /dev/null
+++ b/docs/tutorials/building-a-node-with-ldk/setting-up-a-channel-manager.md
@@ -0,0 +1,796 @@
+# Setting up a ChannelManager
+
+The ChannelManager is responsible for several tasks related to managing channel state. This includes keeping track of many channels, sending messages to appropriate channels, creating channels and more.
+
+## Adding a ChannelManager
+
+To add a `ChannelManager` to your application, run:
+
+
+
+
+ ```rust
+ use lightning::ln::channelmanager;
+
+ let channel_manager = ChannelManager::new(
+ &fee_estimator,
+ &chain_monitor,
+ &broadcaster,
+ &logger,
+ &keys_manager,
+ user_config,
+ chain_params,
+);
+ ```
+
+
+
+
+ ```java
+ import org.ldk.batteries.ChannelManagerConstructor
+
+ val channelManagerConstructor = ChannelManagerConstructor(
+ Network.LDKNetwork_Regtest,
+ userConfig,
+ latestBlockHash,
+ latestBlockHeight,
+ keysManager.as_KeysInterface(),
+ feeEstimator,
+ chainMonitor,
+ router,
+ txBroadcaster,
+ logger
+ );
+ ```
+
+
+
+
+There are a few dependencies needed to get this working. Let's walk through setting up each one so we can plug them into our `ChannelManager`.
+
+### Initialize the `FeeEstimator`
+
+**What it's used for:** estimating fees for on-chain transactions that LDK wants broadcasted.
+
+
+
+
+ ```rust
+ struct YourFeeEstimator();
+
+ impl FeeEstimator for YourFeeEstimator {
+ fn get_est_sat_per_1000_weight(
+ &self, confirmation_target: ConfirmationTarget,
+ ) -> u32 {
+ match confirmation_target {
+ ConfirmationTarget::Background => // fetch background feerate,
+ ConfirmationTarget::Normal => // fetch normal feerate (~6 blocks)
+ ConfirmationTarget::HighPriority => // fetch high priority feerate
+ }
+ }
+ }
+
+ let fee_estimator = YourFeeEstimator();
+ ```
+
+
+
+
+
+ ```java
+ object YourFeeEstimator : FeeEstimatorInterface {
+ override fun get_est_sat_per_1000_weight(confirmationTarget: ConfirmationTarget?): Int {
+ if (confirmationTarget == ConfirmationTarget.LDKConfirmationTarget_Background) {
+ //
+ }
+
+ if (confirmationTarget == ConfirmationTarget.LDKConfirmationTarget_Normal) {
+ //
+ }
+
+ if (confirmationTarget == ConfirmationTarget.LDKConfirmationTarget_HighPriority) {
+ //
+ }
+ // return default fee rate
+ }
+}
+
+val feeEstimator: FeeEstimator = FeeEstimator.new_impl(YourFeeEstimator)
+```
+
+
+
+
+**Implementation notes:**
+1. Fees must be returned in: satoshis per 1000 weight units
+2. Fees must be no smaller than 253 (equivalent to 1 satoshi/vbyte, rounded up)
+3. To reduce network traffic, you may want to cache fee results rather than
+retrieving fresh ones every time
+
+**Dependencies:** *none*
+
+**References:** [Rust `FeeEstimator` docs](https://docs.rs/lightning/*/lightning/chain/chaininterface/trait.FeeEstimator.html), [Java `FeeEstimator` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/FeeEstimator.java)
+
+### Initialize the `Logger`
+**What it's used for:** LDK logging
+
+
+
+
+ ```rust
+ struct YourLogger();
+
+ impl Logger for YourLogger {
+ fn log(&self, record: &Record) {
+ let raw_log = record.args.to_string();
+ let log = format!(
+ "{} {:<5} [{}:{}] {}\n",
+ OffsetDateTime::now_utc().format("%F %T"),
+ record.level.to_string(),
+ record.module_path,
+ record.line,
+ raw_log
+ );
+ //
+ }
+ }
+
+ let logger = YourLogger();
+ ```
+
+
+
+
+
+ ```java
+ object YourLogger : LoggerInterface {
+ override fun log(record: Record?) {
+ //
+ }
+ }
+
+ val logger: Logger = Logger.new_impl(YourLogger)
+ ```
+
+
+
+
+**Implementation notes:** you'll likely want to write the logs to a file for debugging purposes.
+
+**Dependencies:** *none*
+
+**References:** [Rust `Logger` docs](https://docs.rs/lightning/*/lightning/util/logger/trait.Logger.html), [Java `Logger` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Logger.java)
+
+### Initialize the `BroadcasterInterface`
+**What it's used for:** broadcasting various transactions to the bitcoin network
+
+
+
+
+ ```rust
+ struct YourTxBroadcaster();
+
+ impl BroadcasterInterface for YourTxBroadcaster {
+ fn broadcast_transaction(&self, tx: &Transaction) {
+ //
+ }
+ }
+
+ let broadcaster = YourTxBroadcaster();
+ ```
+
+
+
+
+
+ ```java
+ object YourTxBroadcaster: BroadcasterInterface.BroadcasterInterfaceInterface {
+ override fun broadcast_transaction(tx: ByteArray?) {
+ //
+ }
+ }
+
+ val txBroadcaster: BroadcasterInterface = BroadcasterInterface.new_impl(YourTxBroadcaster)
+ ```
+
+
+
+
+**Dependencies:** *none*
+
+**References:** [Rust `BroadcasterInterface` docs](https://docs.rs/lightning/*/lightning/chain/chaininterface/trait.BroadcasterInterface.html), [Java `BroadcasterInterface` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/BroadcasterInterface.java)
+
+### Initialize `Persist`
+**What it's used for:** persisting `ChannelMonitor`s, which contain crucial channel data, in a timely manner
+
+
+
+
+ ```rust
+ struct YourPersister();
+
+ impl Persist for YourPersister {
+ fn persist_new_channel(
+ &self, id: OutPoint, data: &ChannelMonitor
+ ) -> Result<(), ChannelMonitorUpdateErr> {
+ //
+ // Note that monitor.encode() will get you the ChannelMonitor as a
+ // Vec.
+ }
+
+ fn update_persisted_channel(
+ &self,
+ id: OutPoint,
+ update: &ChannelMonitorUpdate,
+ data: &ChannelMonitor
+ ) -> Result<(), ChannelMonitorUpdateErr> {
+ //
+ }
+ }
+
+ let persister = YourPersister();
+ ```
+
+
+
+
+ ```java
+ object YourPersister: Persist.PersistInterface {
+ override fun persist_new_channel(
+ id: OutPoint?, data: ChannelMonitor?, updateId: MonitorUpdateId?
+ ): Result_NoneChannelMonitorUpdateErrZ? {
+ val channelMonitorBytes = data.write()
+ //
+ }
+
+ override fun update_persisted_channel(
+ id: OutPoint?, update: ChannelMonitorUpdate?, data: ChannelMonitor?,
+ updateId: MonitorUpdateId
+ ): Result_NoneChannelMonitorUpdateErrZ? {
+ val channelMonitorBytes = data.write()
+ //
+ }
+ }
+
+ val persister: Persist = Persist.new_impl(YourPersister)
+ ```
+
+
+
+
+
+
+
+ ```rust
+ use lightning_persister::FilesystemPersister; // import LDK sample persist module
+
+ let persister = FilesystemPersister::new(ldk_data_dir_path);
+ ```
+
+
+
+
+**Implementation notes:**
+* `ChannelMonitor`s are objects which are capable of
+responding to on-chain events for a given channel. Thus, you will have one
+`ChannelMonitor` per channel. They are persisted in real-time and the `Persist`
+methods will block progress on sending or receiving payments until they return.
+You must ensure that `ChannelMonitor`s are durably persisted to disk before
+returning or you may lose funds.
+* If you implement a custom persister, it's important to read the trait docs (linked in References) to make sure you satisfy the API requirements, particularly for `update_persisted_channel`
+
+**Dependencies:** *none*
+
+**References:** [Rust `Persister` docs](https://docs.rs/lightning/*/lightning/chain/chainmonitor/trait.Persist.html), [Java `Persister` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Persist.java)
+
+### Optional: Initialize the Transaction `Filter`
+**You must follow this step if:** you are *not* providing full blocks to LDK,
+i.e. if you're using BIP 157/158 or Electrum as your chain backend
+
+**What it's used for:** if you are not providing full blocks, LDK uses this
+object to tell you what transactions and outputs to watch for on-chain. You'll
+inform LDK about these transactions/outputs in Step 14.
+
+
+
+
+
+ ```rust
+ struct YourTxFilter();
+
+ impl Filter for YourTxFilter {
+ fn register_tx(&self, txid: &Txid, script_pubkey: &Script) {
+ //
+ }
+
+ fn register_output(&self, output: WatchedOutput) ->
+ Option<(usize, Transaction)> {
+ //
+ }
+ }
+
+ let filter = YourTxFilter();
+ ```
+
+
+
+
+ ```java
+ object YourTxFilter : Filter.FilterInterface {
+ override fun register_tx(txid: ByteArray, script_pubkey: ByteArray) {
+ //
+ }
+
+ override fun register_output(output: WatchedOutput) {
+ //
+ }
+ }
+
+ val txFilter: Filter = Filter.new_impl(YourTxFilter)
+ ```
+
+
+
+**Implementation notes:** see the [Blockchain Data](/blockchain_data/introduction.md) guide for more info
+
+**Dependencies:** *none*
+
+**References:** [Rust `Filter` docs](https://docs.rs/lightning/*/lightning/chain/trait.Filter.html), [Java `Filter` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Filter.java)
+
+### Initialize the `ChainMonitor`
+**What it's used for:** tracking one or more `ChannelMonitor`s and using them to monitor the chain for lighting transactions that are relevant to our node, and broadcasting transactions if need be.
+
+
+
+
+ ```rust
+ let filter: Option> = // leave this as None or insert the Filter trait object
+
+ let chain_monitor = ChainMonitor::new(filter, &broadcaster, &logger, &fee_estimator, &persister);
+ ```
+
+
+
+
+ ```java
+ val filter : Filter = // leave this as `null` or insert the Filter object.
+
+ val chainMonitor = ChainMonitor.of(filter, txBroadcaster, logger, feeEstimator, persister)
+ ```
+
+
+
+**Implementation notes:** `Filter` must be non-`None` if you're using Electrum or BIP 157/158 as your chain backend
+
+**Dependencies:** `FeeEstimator`, `Logger`, `BroadcasterInterface`, `Persist`
+
+**Optional dependency:** `Filter`
+
+**References:** [Rust `ChainMonitor` docs](https://docs.rs/lightning/*/lightning/chain/chainmonitor/struct.ChainMonitor.html), [Java `ChainMonitor` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/ChainMonitor.java)
+
+### Initialize the `KeysManager`
+**What it's used for:** providing keys for signing Lightning transactions
+
+
+
+
+ ```rust
+ let keys_seed_path = format!("{}/keys_seed", ldk_data_dir.clone());
+
+ // If we're restarting and already have a key seed, read it from disk. Else,
+ // create a new one.
+ let keys_seed = if let Ok(seed) = fs::read(keys_seed_path.clone()) {
+ assert_eq!(seed.len(), 32);
+ let mut key = [0; 32];
+ key.copy_from_slice(&seed);
+ key
+ } else {
+ let mut key = [0; 32];
+ thread_rng().fill_bytes(&mut key);
+ match File::create(keys_seed_path.clone()) {
+ Ok(mut f) => {
+ f.write_all(&key)
+ .expect("Failed to write node keys seed to disk");
+ f.sync_all().expect("Failed to sync node keys seed to disk");
+ }
+ Err(e) => {
+ println!(
+ "ERROR: Unable to create keys seed file {}: {}",
+ keys_seed_path, e
+ );
+ return;
+ }
+ }
+ key
+ };
+ let cur = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
+ let keys_manager = KeysManager::new(&keys_seed, cur.as_secs(), cur.subsec_nanos());
+
+ ```
+
+
+
+
+
+ ```java
+ val keySeed = ByteArray(32)
+ //
+ val keysManager = KeysManager.of(
+ keySeed,
+ System.currentTimeMillis() / 1000,
+ (System.currentTimeMillis() * 1000).toInt()
+ )
+ ```
+
+
+
+**Implementation notes:**
+* See the [Key Management](/key_management.md) guide for more info
+* Note that you must write the `key_seed` you give to the `KeysManager` on
+ startup to disk, and keep using it to initialize the `KeysManager` every time
+ you restart. This `key_seed` is used to derive your node's secret key (which
+ corresponds to its node pubkey) and all other secret key material.
+* The current time is part of the `KeysManager`'s parameters because it is used to derive
+random numbers from the seed where required, to ensure all random
+generation is unique across restarts.
+
+**Dependencies:** random bytes
+
+**References:** [Rust `KeysManager` docs](https://docs.rs/lightning/*/lightning/chain/keysinterface/struct.KeysManager.html), [Java `KeysManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/KeysManager.java)
+
+### Read `ChannelMonitor` state from disk
+
+**What it's used for:** if LDK is restarting and has at least 1 channel, its `ChannelMonitor`s will need to be (1) fed to the `ChannelManager` and (2) synced to chain.
+
+
+
+
+ ```rust
+ // Use LDK's sample persister module provided method
+ let mut channel_monitors =
+ persister.read_channelmonitors(keys_manager.clone()).unwrap();
+
+ // If you are using Electrum or BIP 157/158, you must call load_outputs_to_watch
+ // on each ChannelMonitor to prepare for chain synchronization in Step 9.
+ for chan_mon in channel_monitors.iter() {
+ chan_mon.load_outputs_to_watch(&filter);
+ }
+ ```
+
+
+
+
+
+ ```java
+ // Initialize the hashmap where we'll store the `ChannelMonitor`s read from disk.
+ // This hashmap will later be given to the `ChannelManager` on initialization.
+ var channelMonitors = arrayOf();
+
+ val channelMonitorList = ArrayList()
+ channelMonitorFiles.iterator().forEach {
+ val channelMonitorBytes = it.hexStringToByteArray();
+ channelMonitorList.add(channelMonitorBytes);
+ }
+ channelMonitors = channelMonitorList.toTypedArray();
+ ```
+
+
+
+**Dependencies:** `KeysManager`
+
+**References:** [Rust `load_outputs_to_watch` docs](https://docs.rs/lightning/*/lightning/chain/channelmonitor/struct.ChannelMonitor.html#method.load_outputs_to_watch)
+
+### Initialize the `ChannelManager`
+**What it's used for:** managing channel state
+
+
+
+
+ ```rust
+ let user_config = UserConfig::default();
+
+ /* RESTARTING */
+ let (channel_manager_blockhash, mut channel_manager) = {
+ let channel_manager_file =
+ fs::File::open(format!("{}/manager", ldk_data_dir.clone())).unwrap();
+
+ // Use the `ChannelMonitors` we read from disk in Step 7.
+ let mut channel_monitor_mut_references = Vec::new();
+ for (_, channel_monitor) in channel_monitors.iter_mut() {
+ channel_monitor_mut_references.push(channel_monitor);
+ }
+ let read_args = ChannelManagerReadArgs::new(
+ &keys_manager,
+ &fee_estimator,
+ &chain_monitor,
+ &broadcaster,
+ &logger,
+ user_config,
+ channel_monitor_mut_references,
+ );
+ <(BlockHash, ChannelManager)>::read(&mut channel_manager_file, read_args)
+ .unwrap()
+ };
+
+ /* FRESH CHANNELMANAGER */
+
+ let (channel_manager_blockhash, mut channel_manager) = {
+ let best_blockhash = // insert the best blockhash you know of
+ let best_chain_height = // insert the height corresponding to best_blockhash
+ let chain_params = ChainParameters {
+ network: Network::Testnet, // substitute this with your network
+ best_block: BestBlock::new(best_blockhash, best_chain_height),
+ };
+ let fresh_channel_manager = ChannelManager::new(
+ &fee_estimator,
+ &chain_monitor,
+ &broadcaster,
+ &logger,
+ &keys_manager,
+ user_config,
+ chain_params,
+ );
+ (best_blockhash, fresh_channel_manager)
+ };
+ ```
+
+
+
+
+ ```java
+ if (serializedChannelManager != "") {
+ // loading from disk (restarting)
+ channelManagerConstructor = ChannelManagerConstructor(
+ serializedChannelManager,
+ channelMonitors,
+ userConfig,
+ keysManager.as_KeysInterface(),
+ feeEstimator,
+ chainMonitor,
+ txFilter,
+ router.write(),
+ txBroadcaster,
+ logger
+ );
+ } else {
+ // fresh start
+ channelManagerConstructor = ChannelManagerConstructor(
+ Network.LDKNetwork_Regtest,
+ userConfig,
+ latestBlockHash,
+ latestBlockHeight,
+ keysManager.as_KeysInterface(),
+ feeEstimator,
+ chainMonitor,
+ router,
+ txBroadcaster,
+ logger
+ );
+ }
+ ```
+
+
+
+**Implementation notes:** No methods should be called on `ChannelManager` until
+*after* the `ChannelMonitor`s and `ChannelManager` are synced to the chain tip (next step).
+
+**Dependencies:** `KeysManager`, `FeeEstimator`, `ChainMonitor`, `BroadcasterInterface`, `Logger`
+* If restarting: `ChannelMonitor`s and `ChannelManager` bytes from Step 7 and Step 18 respectively
+
+**References:** [Rust `ChannelManager` docs](https://docs.rs/lightning/*/lightning/ln/channelmanager/struct.ChannelManager.html), [Java `ChannelManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/ChannelManager.java)
+
+### Sync `ChannelMonitor`s and `ChannelManager` to chain tip
+**What it's used for:** this step is only necessary if you're restarting and have open channels. This step ensures that LDK channel state is up-to-date with the bitcoin blockchain
+
+**Example:**
+
+
+
+
+ ```rust
+ // Full Blocks or BIP 157/158
+
+ use lightning_block_sync::init;
+ use lightning_block_sync::poll;
+ use lightning_block_sync::UnboundedCache;
+
+ impl lightning_block_sync::BlockSource for YourChainBackend {
+ fn get_header<'a>(
+ &'a mut self, header_hash: &'a BlockHash, height_hint: Option,
+ ) -> AsyncBlockSourceResult<'a, BlockHeaderData> {
+ //
+ }
+
+ fn get_block<'a>(
+ &'a mut self, header_hash: &'a BlockHash,
+ ) -> AsyncBlockSourceResult<'a, Block> {
+ //
+ }
+
+ fn get_best_block<'a>(&'a mut self) ->
+ AsyncBlockSourceResult<(BlockHash, Option)> {
+ //
+ }
+ }
+
+ let block_source = YourChainBackend::new();
+
+ let mut chain_listener_channel_monitors = Vec::new();
+ let mut cache = UnboundedCache::new();
+ let mut chain_tip: Option = None;
+ let mut chain_listeners = vec![(
+ channel_manager_blockhash,
+ &mut channel_manager as &mut dyn chain::Listen,
+ )];
+
+ for (blockhash, channel_monitor) in channel_monitors.drain(..) {
+ let outpoint = channel_monitor.get_funding_txo().0;
+ chain_listener_channel_monitors.push((
+ blockhash,
+ (
+ channel_monitor,
+ &broadcaster,
+ &fee_estimator,
+ &logger,
+ ),
+ outpoint,
+ ));
+ }
+
+ for monitor_listener_info in chain_listener_channel_monitors.iter_mut()
+ {
+ chain_listeners.push((
+ monitor_listener_info.0,
+ &mut monitor_listener_info.1 as &mut dyn chain::Listen,
+ ));
+ }
+
+ // Save the chain tip to be used in future steps
+ chain_tip = Some(
+ init::synchronize_listeners(
+ &mut block_source,
+ Network::Testnet,
+ &mut cache,
+ chain_listeners,
+ )
+ .await
+ .unwrap(),
+ );
+ ```
+
+
+
+
+
+ ```java
+ // Electrum/Esplora
+
+ // Retrieve transaction IDs to check the chain for un-confirmation.
+ val relevantTxIdsFromChannelManager: Array = channelManager .as_Confirm().get_relevant_txids()
+ val relevantTxIdsFromChannelManager: Array = chainMonitor.as_Confirm().get_relevant_txids()
+ val relevantTxIds = relevantTxIdsFromChannelManager + relevantTxIdsFromChainMonitor
+
+ val unconfirmedTxs: Array = //
+
+ for (txid in unconfirmedTxs) {
+ channelManager .transaction_unconfirmed(txid)
+ chainMonitor.transaction_unconfirmed(txid)
+ }
+
+ // Retrieve transactions and outputs that were registered through the `Filter`
+ // interface.
+
+ // If any of these txs/outputs were confirmed on-chain, then:
+ val header: Array = // insert block header from the block with confirmed tx/output
+ val height: Int = // insert block height of `header`
+ val txIndex: Long = // insert tx index in block
+ val serializedTx: Array = // insert tx hex as byte array
+ val tx: TwoTuple_usizeTransactionZ = TwoTuple_usizeTransactionZ.of(txIndex, serializedTx);
+
+ // Marshall all TwoTuples you built right above into an array
+ val txList = arrayOf(TwoTuple_usizeTransactionZ.of(tx.., ..));
+
+ channelManager.transactions_confirmed(header, height, txList);
+ chainMonitor.transactions_confirmed(header, height, txList);
+
+ val bestHeader: Array = //
+ val bestHeight: Int = //
+ channelManager.update_best_block(bestHeader, bestHeight);
+ chainMonitor.update_best_block(bestHeader, bestHeight);
+
+ // Finally, tell LDK that chain sync is complete. This will also spawn several
+ // background threads to handle networking and event processing.
+ channelManagerConstructor.chain_sync_completed(customEventHandler);
+ ```
+
+
+
+
+**Implementation notes:**
+
+There are 2 main options for synchronizing to chain on startup:
+
+**Full Blocks or BIP 157/158**
+
+If you are connecting full blocks or using BIP 157/158, then it is recommended to use
+LDK's `lightning_block_sync` sample module as in the example above: the high-level steps that must be done for both `ChannelManager` and each `ChannelMonitor` are as follows:
+
+1. Get the last blockhash that each object saw.
+ * Receive the latest block hash when through [deserializtion](https://docs.rs/lightning/*/lightning/ln/channelmanager/struct.ChannelManagerReadArgs.html) of the `ChannelManager` via `read()`
+ * Each `ChannelMonitor`'s is in `channel_manager.channel_monitors`, as the 2nd element in each tuple
+2. For each object, if its latest known blockhash has been reorged out of the chain, then disconnect blocks using `channel_manager.as_Listen().block_disconnected(..)` or `channel_monitor.block_disconnected(..)` until you reach the last common ancestor with the main chain.
+3. For each object, reconnect blocks starting from the common ancestor until it gets to your best known chain tip using `channel_manager.as_Listen().block_connected(..)` and/or `channel_monitor.block_connected(..)`.
+4. Call `channel_manager.chain_sync_completed(..)` to complete the initial sync process.
+
+**Esplora**
+
+Alternatively, you can use LDK's `lightning-transaction-sync` crate. This provides utilities for syncing LDK via the transaction-based `Confirm` interface.
+
+**Electrum/Esplora**
+
+Otherwise, you can use LDK's `Confirm` interface directly as in the examples above. The high-level steps are as follows:
+ 1. Tell LDK about relevant confirmed and unconfirmed transactions.
+ 2. Tell LDK what your best known block header and height is.
+ 3. Call `channel_manager_constructor.chain_sync_completed(..)` to complete the initial sync process.
+
+**References:** [Rust `Confirm` docs](https://docs.rs/lightning/*/lightning/chain/trait.Confirm.html), [Rust `Listen` docs](https://docs.rs/lightning/*/lightning/chain/trait.Listen.html), [Rust `lightning_block_sync` module docs](https://docs.rs/lightning-block-sync/*/lightning_block_sync/), [Rust `lightning_transaction_sync` module docs](https://docs.rs/lightning-transaction-sync/*/lightning_transaction_sync/)
+
+**Dependencies:** `ChannelManager`, `ChainMonitor`, `ChannelMonitor`s
+* If providing providing full blocks or BIP 157/158: set of `ChannelMonitor`s
+* If using Electrum: `ChainMonitor`
+
+### Optional: Initialize `P2PGossipSync or RapidGossipSync`
+
+**You must follow this step if:** you need LDK to provide routes for sending payments (i.e. you are *not* providing your own routes)
+
+**What it's used for:** generating routes to send payments over
+
+
+
+
+
+```rust
+let genesis = genesis_block(Network::Testnet).header.block_hash();
+let network_graph_path = format!("{}/network_graph", ldk_data_dir.clone());
+let network_graph = Arc::new(disk::read_network(Path::new(&network_graph_path), genesis, logger.clone()));
+let gossip_sync = Arc::new(P2PGossipSync::new(
+ Arc::clone(&network_graph),
+ None::>,
+ logger.clone(),
+));
+```
+
+
+
+
+
+```java
+val genesisBlock : BestBlock = BestBlock.from_genesis(Network.LDKNetwork_Testnet)
+val genesisBlockHash : String = byteArrayToHex(genesisBlock.block_hash())
+
+val serializedNetworkGraph = // Read network graph bytes from file
+val networkGraph : NetworkGraph = NetworkGraph.read(serializedNetworkGraph, logger)
+val p2pGossip : P2PGossipSync = P2PGossipSync.of(networkGraph, Option_AccessZ.none(), logger)
+```
+
+
+
+
+
+
+
+**Implementation notes:** this struct is not required if you are providing your own routes. It will be used internally in `ChannelManager` to build a `NetworkGraph`. Other networking options are: `LDKNetwork_Bitcoin`, `LDKNetwork_Regtest` and `LDKNetwork_Testnet`
+
+**Dependencies:** `Logger`
+
+**Optional dependency:** `Access`, a source of chain information. Recommended to be able to verify channels before adding them to the internal network graph.
+
+**References:** [Rust `P2PGossipSync` docs](https://docs.rs/lightning/*/lightning/routing/gossip/struct.P2PGossipSync.html), [`Access` docs](https://docs.rs/lightning/*/lightning/chain/trait.Access.html), [Java `P2PGossipSync` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/P2PGossipSync.java), [Rust `RapidGossipSync` docs](https://docs.rs/lightning-rapid-gossip-sync/*/lightning_rapid_gossip_sync/), [Java `RapidGossipSync` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/RapidGossipSync.java)
+
diff --git a/docs/tutorials/building-a-node-with-ldk/setting-up-a-peer-manager.md b/docs/tutorials/building-a-node-with-ldk/setting-up-a-peer-manager.md
new file mode 100644
index 000000000..f1aeca96f
--- /dev/null
+++ b/docs/tutorials/building-a-node-with-ldk/setting-up-a-peer-manager.md
@@ -0,0 +1,51 @@
+# Setting up a PeerManager
+
+The Peer Manager is responsible for managing a set of peer connections and all data associated with those peers.
+
+
+## Adding a PeerManager
+
+To add a PeerManager to your application, run:
+
+
+
+
+ ```rust
+ use lightning::ln::peer_handler::{PeerManager};
+
+ let mut ephemeral_bytes = [0; 32];
+ rand::thread_rng().fill_bytes(&mut ephemeral_bytes);
+
+ let lightning_msg_handler = MessageHandler {
+ chan_handler: &channel_manager,
+ route_handler: &gossip_sync,
+ };
+
+ let ignoring_custom_msg_handler = IgnoringMessageHandler {};
+ let peer_manager = PeerManager::new(
+ lightning_msg_handler,
+ keys_manager.get_node_secret(),
+ &ephemeral_bytes,
+ &logger,
+ &ignoring_custom_msg_handler,
+ );
+ ```
+
+
+
+
+ ```java
+ import org.ldk.structs.PeerManager
+
+ val peerManager: PeerManager = channelManagerConstructor.peer_manager;
+ ```
+
+
+
+
+**Implementation notes:** if you did not initialize `P2PGossipSync` in the previous step, you can initialize your own struct (which can be a dummy struct) that implements `RoutingMessageHandler`
+
+**Dependencies:** `ChannelManager`, `RoutingMessageHandler`, `KeysManager`, random bytes, `Logger`
+
+**References:** [Rust `PeerManager` docs](https://docs.rs/lightning/*/lightning/ln/peer_handler/struct.PeerManager.html), [Rust `RoutingMessageHandler` docs](https://docs.rs/lightning/*/lightning/ln/msgs/trait.RoutingMessageHandler.html), [Java `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/PeerManager.java), [Java `RoutingMessageHandler` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/RoutingMessageHandler.java)
+
diff --git a/docs/tutorials/getting-started.md b/docs/tutorials/getting-started.md
index 3a28a8e09..8eafa5c45 100644
--- a/docs/tutorials/getting-started.md
+++ b/docs/tutorials/getting-started.md
@@ -9,7 +9,7 @@ MacOS, Windows and Linux are supported.
## Installation
To add LDK to a project, run:
-
+
```toml
@@ -26,22 +26,9 @@ To add LDK to a project, run:
```
-
-
- ```xml
-
-
-
- org.lightningdevkit
- ldk-java
- {VERSION}
-
- ```
+
- ```kotlin
+ ```java
/*
For Gradle, add the following dependency to your build.gradle and replace {VERSION} with
the version number you want to use.
@@ -52,12 +39,7 @@ To add LDK to a project, run:
implementation 'org.lightningdevkit:ldk-java:{VERSION}'
// ...
}
- ```
-
-
-
- ```kotlin
/* To include the LDK Kotlin bindings in an Android project download the latest binary from https://github.com/lightningdevkit/ldk-garbagecollected/releases and place it in your libs directory.
Then add to your build.gradle file:
*/
@@ -70,6 +52,7 @@ To add LDK to a project, run:
```
+
```javascript
@@ -81,7 +64,7 @@ To add LDK to a project, run:
Example usage after installation is complete:
-
+
```rust
@@ -89,16 +72,10 @@ Example usage after installation is complete:
```
-
- ```java
- import org.ldk.structs.FeeEstimator
- ```
-
-
- ```kotlin
+ ```java
import org.ldk.structs.FeeEstimator
```