Skip to content

Commit 3bfe804

Browse files
committed
Initial draft of blockchain data guide
1 parent afdcbf3 commit 3bfe804

File tree

3 files changed

+189
-1
lines changed

3 files changed

+189
-1
lines changed

docs/assets/ldk-block-processing.svg

Lines changed: 1 addition & 0 deletions
Loading

docs/blockdata.md

Lines changed: 185 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,188 @@ id: blockdata
33
title: Blockchain Data
44
---
55

6-
TODO
6+
import Tabs from '@theme/Tabs';
7+
import TabItem from '@theme/TabItem';
8+
9+
## Introduction
10+
11+
In this guide, we'll explore how to provide chain data to LDK upon startup and
12+
as new blocks are mined. This allows LDK to maintain channel state and monitor
13+
for on-chain channel activity.
14+
15+
## Overview
16+
17+
LDK maintains channels with your node's peers during the course of node
18+
operation. When a new channel is opened, the `ChannelManager` will keep track of
19+
the channel's state and tell the `ChainMonitor` that a new channel should be
20+
watched. The `ChainMonitor` does so by maintaining a `ChannelMonitor` for each
21+
channel.
22+
23+
When a new block is mined, it is connected to the chain while other blocks may
24+
be disconnected. LDK will process such events as they are fed into it from a
25+
`BlockSource` by:
26+
27+
* Updating channel state
28+
* Signaling back transactions to filter
29+
* Broadcasting transactions if necessary
30+
31+
We will walk through this process as depicted here:
32+
33+
![LDK block processing](assets/ldk-block-processing.svg)
34+
35+
## Block Source
36+
37+
Initially, our node doesn't have any channels and so has no data to monitor for
38+
on-chain. When a channel is opened with a peer, the `ChannelManager` creates a
39+
`ChannelMonitor` and passes it to the `ChainMonitor` to watch.
40+
41+
At this point, LDK needs to be fed chain data of interest so that it can respond
42+
accordingly. It supports receiving either full blocks or pre-filtered blocks.
43+
Block data can sourced from anywhere, but it is your responsibility to ensure
44+
that the necessary `block_connected` and `block_disconnected` methods are called
45+
on `ChannelManager` and `ChainMonitor`.
46+
47+
LDK comes with a `lightning-block-sync` utility that handles polling a block
48+
source for the best chain tip, detecting chain forks, and notifying listeners
49+
when blocks are connected and disconnected. It can be configured to:
50+
51+
* Poll a custom `BlockSource`
52+
* Notify `ChannelManager` and `ChainMonitor` of block events
53+
54+
It is your choice as to whether you use this utility or your own to feed the
55+
required chain data to LDK. If you choose to use it, you will need to implement
56+
the `BlockSource` interface or use one of the samples that it provides.
57+
58+
:::note
59+
Currently, `lightning-block-sync` is only available in Rust.
60+
:::
61+
62+
Implementing the `BlockSource` interface requires defining methods for fetching
63+
headers, blocks, and the best block hash.
64+
65+
<Tabs
66+
defaultValue="rust"
67+
values={[
68+
{ label: 'Rust', value: 'rust', },
69+
{ label: 'Java', value: 'java', },
70+
]
71+
}>
72+
<TabItem value="rust">
73+
74+
```rust
75+
impl BlockSource for Blockchain {
76+
fn get_header<'a>(&'a mut self, header_hash: &'a BlockHash, _height: Option<u32>) -> AsyncBlockSourceResult<'a, BlockHeaderData> {
77+
// <insert code for fetching block headers>
78+
}
79+
80+
fn get_block<'a>(&'a mut self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, Block> {
81+
// <insert code for fetching block>
82+
}
83+
84+
fn get_best_block<'a>(&'a mut self) -> AsyncBlockSourceResult<'a, (BlockHash, Option<u32>)> {
85+
// <insert code for fetching the best block hash>
86+
}
87+
}
88+
```
89+
90+
</TabItem>
91+
<TabItem value="java">
92+
93+
```java
94+
// TODO
95+
```
96+
97+
</TabItem>
98+
</Tabs>
99+
100+
For instance, you may implement this interface by querying Bitcoin Core's JSON
101+
RPC interface, which happens to be a sample implementation provided by
102+
`lightning-block-sync`.
103+
104+
Let's walk through the use case where LDK receives full blocks.
105+
106+
### Full Blocks
107+
108+
If your Lightning node is backed by a Bitcoin full node, the operation is
109+
straight forward: call the appropriate methods on `ChannelManager` and
110+
`ChainMonitor` as blocks are connected and disconnected. LDK will handle the
111+
rest!
112+
113+
So what happens? The `ChannelManager` examines the blocks transactions and
114+
updates the internal channel state as needed. The `ChainMonitor` will detect
115+
any spends of the channel funding transaction or any pertinent transaction
116+
outputs, tracking them as necessary.
117+
118+
If necessary, LDK will broadcast a transaction on your behalf. More on that
119+
later. For now, let's look at the more interesting case of pre-filtered blocks.
120+
121+
### Pre-filtered Blocks
122+
123+
For environments that are resource constrained, receiving and processing all
124+
transaction data may not be feasible. LDK handles this case by signaling back
125+
with which transactions and outputs it is interested in. This information can
126+
then be used to filter blocks prior to sending them to your node.
127+
128+
For example, if your block source is an Electrum client, you can pass along this
129+
information to it. Or if you are making use of a BIP 157 client, you can check
130+
if a block contains relevant transactions before fetching it.
131+
132+
So how does this work in practice? `ChainMonitor` is parameterized by an
133+
optional type that implements `chain::Filter`:
134+
135+
<Tabs
136+
defaultValue="rust"
137+
values={[
138+
{ label: 'Rust', value: 'rust', },
139+
{ label: 'Java', value: 'java', },
140+
]
141+
}>
142+
<TabItem value="rust">
143+
144+
```rust
145+
impl chain::Filter for Blockchain {
146+
fn register_tx(&self, txid: &Txid, script_pubkey: &Script) {
147+
// <insert code for you to watch for this transaction on-chain>
148+
}
149+
150+
fn register_output(&self, outpoint: &OutPoint, script_pubkey: &Script) {
151+
// <insert code for you to watch for this output on-chain>
152+
}
153+
}
154+
```
155+
156+
</TabItem>
157+
<TabItem value="java">
158+
159+
```java
160+
Filter tx_filter = Filter.new_impl(new Filter.FilterInterface() {
161+
@Override
162+
public void register_tx(byte[] txid, byte[] script_pubkey) {
163+
// <insert code for you to watch for this transaction on-chain>
164+
}
165+
166+
@Override
167+
void register_output(OutPoint outpoint, byte[] script_pubkey) {
168+
// <insert code for you to watch for this output on-chain>
169+
}
170+
});
171+
```
172+
173+
</TabItem>
174+
</Tabs>
175+
176+
When this is provided, `ChainMonitor` will call back to the filter as channels
177+
are opened and blocks connected. This gives the opportunity for the source to
178+
pre-filter blocks as desired.
179+
180+
Regardless, when a block is connected, its header must be processed by LDK.
181+
182+
## Transaction Broadcast
183+
184+
Inevitably, LDK will need to broadcast transactions on your behalf. As you
185+
notify it of blocks, it will determine if it should broadcast a transaction and
186+
do so using an implementation of `BroadcasterInterface` that you have provided.
187+
188+
And as those transactions or those from your peers are confirmed on-chain, they
189+
will be likewise processed when notified of a connected block. Thus, continuing
190+
the cycle.

docusaurus.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ module.exports = {
7575
],
7676
copyright: `Copyright © ${new Date().getFullYear()} Square Crypto, Inc.`,
7777
},
78+
prism: {
79+
additionalLanguages: ['rust'],
80+
},
7881
},
7982
presets: [
8083
[

0 commit comments

Comments
 (0)