Skip to content

Commit dddbf67

Browse files
Merge pull request #3 from jkczyz/2021-02-blockdata
Initial draft of blockchain data guide
2 parents c404bd7 + df908d5 commit dddbf67

File tree

3 files changed

+190
-1
lines changed

3 files changed

+190
-1
lines changed

docs/assets/ldk-block-processing.svg

Lines changed: 1 addition & 0 deletions
Loading

docs/blockdata.md

Lines changed: 186 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,189 @@ 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 hence has no data to monitor
38+
for on-chain. When a channel is opened with a peer, the `ChannelManager` creates
39+
a `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`. This allows them to update channel state
46+
and respond to on-chain events, respectively.
47+
48+
LDK comes with a `lightning-block-sync` utility that handles polling a block
49+
source for the best chain tip, detecting chain forks, and notifying listeners
50+
when blocks are connected and disconnected. It can be configured to:
51+
52+
* Poll a custom `BlockSource`
53+
* Notify `ChannelManager` and `ChainMonitor` of block events
54+
55+
It is your choice as to whether you use this utility or your own to feed the
56+
required chain data to LDK. If you choose to use it, you will need to implement
57+
the `BlockSource` interface or use one of the samples that it provides.
58+
59+
:::note
60+
Currently, `lightning-block-sync` is only available in Rust.
61+
:::
62+
63+
Implementing the `BlockSource` interface requires defining methods for fetching
64+
headers, blocks, and the best block hash.
65+
66+
<Tabs
67+
defaultValue="rust"
68+
values={[
69+
{ label: 'Rust', value: 'rust', },
70+
{ label: 'Java', value: 'java', },
71+
]
72+
}>
73+
<TabItem value="rust">
74+
75+
```rust
76+
impl BlockSource for Blockchain {
77+
fn get_header<'a>(&'a mut self, header_hash: &'a BlockHash, _height: Option<u32>) -> AsyncBlockSourceResult<'a, BlockHeaderData> {
78+
// <insert code for fetching block headers>
79+
}
80+
81+
fn get_block<'a>(&'a mut self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, Block> {
82+
// <insert code for fetching block>
83+
}
84+
85+
fn get_best_block<'a>(&'a mut self) -> AsyncBlockSourceResult<'a, (BlockHash, Option<u32>)> {
86+
// <insert code for fetching the best block hash>
87+
}
88+
}
89+
```
90+
91+
</TabItem>
92+
<TabItem value="java">
93+
94+
```java
95+
// TODO
96+
```
97+
98+
</TabItem>
99+
</Tabs>
100+
101+
For instance, you may implement this interface by querying Bitcoin Core's JSON
102+
RPC interface, which happens to be a sample implementation provided by
103+
`lightning-block-sync`.
104+
105+
Let's walk through the use case where LDK receives full blocks.
106+
107+
### Full Blocks
108+
109+
If your Lightning node is backed by a Bitcoin full node, the operation is
110+
straight forward: call the appropriate methods on `ChannelManager` and
111+
`ChainMonitor` as blocks are connected and disconnected. LDK will handle the
112+
rest!
113+
114+
So what happens? The `ChannelManager` examines the blocks transactions and
115+
updates the internal channel state as needed. The `ChainMonitor` will detect
116+
any spends of the channel funding transaction or any pertinent transaction
117+
outputs, tracking them as necessary.
118+
119+
If necessary, LDK will broadcast a transaction on your behalf. More on that
120+
later. For now, let's look at the more interesting case of pre-filtered blocks.
121+
122+
### Pre-filtered Blocks
123+
124+
For environments that are resource constrained, receiving and processing all
125+
transaction data may not be feasible. LDK handles this case by signaling back
126+
which transactions and outputs it is interested in. This information can then be
127+
used to filter blocks prior to sending them to your node.
128+
129+
For example, if your block source is an Electrum client, you can pass along this
130+
information to it. Or if you are making use of a BIP 157 client, you can check
131+
if a block contains relevant transactions before fetching it.
132+
133+
So how does this work in practice? `ChainMonitor` is parameterized by an
134+
optional type that implements `chain::Filter`:
135+
136+
<Tabs
137+
defaultValue="rust"
138+
values={[
139+
{ label: 'Rust', value: 'rust', },
140+
{ label: 'Java', value: 'java', },
141+
]
142+
}>
143+
<TabItem value="rust">
144+
145+
```rust
146+
impl chain::Filter for Blockchain {
147+
fn register_tx(&self, txid: &Txid, script_pubkey: &Script) {
148+
// <insert code for you to watch for this transaction on-chain>
149+
}
150+
151+
fn register_output(&self, outpoint: &OutPoint, script_pubkey: &Script) {
152+
// <insert code for you to watch for this output on-chain>
153+
}
154+
}
155+
```
156+
157+
</TabItem>
158+
<TabItem value="java">
159+
160+
```java
161+
Filter tx_filter = Filter.new_impl(new Filter.FilterInterface() {
162+
@Override
163+
public void register_tx(byte[] txid, byte[] script_pubkey) {
164+
// <insert code for you to watch for this transaction on-chain>
165+
}
166+
167+
@Override
168+
void register_output(OutPoint outpoint, byte[] script_pubkey) {
169+
// <insert code for you to watch for this output on-chain>
170+
}
171+
});
172+
```
173+
174+
</TabItem>
175+
</Tabs>
176+
177+
When this is provided, `ChainMonitor` will call back to the filter as channels
178+
are opened and blocks connected. This gives the opportunity for the source to
179+
pre-filter blocks as desired.
180+
181+
Regardless, when a block is connected, its header must be processed by LDK.
182+
183+
## Transaction Broadcast
184+
185+
Inevitably, LDK will need to broadcast transactions on your behalf. As you
186+
notify it of blocks, it will determine if it should broadcast a transaction and
187+
do so using an implementation of `BroadcasterInterface` that you have provided.
188+
189+
And as those transactions or those from your peers are confirmed on-chain, they
190+
will be likewise processed when notified of a connected block. Thus, continuing
191+
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)