# 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
Adding a ChannelManager
to your application should look something like this:
- Rust
- Kotlin
- Swift
use lightning::ln::channelmanager;
let channel_manager = ChannelManager::new(
&fee_estimator,
&chain_monitor,
&broadcaster,
&router,
&logger,
&entropy_source,
&node_signer,
&signer_provider,
user_config,
chain_params,
current_timestamp
);
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
- Kotlin
- Swift
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,
// You can add the code here for this case
}
ConfirmationTarget::Normal => {
// Fetch normal feerate (~6 blocks)
// You can add the code here for this case
}
ConfirmationTarget::HighPriority => {
// Fetch high priority feerate
// You can add the code here for this case
}
}
}
}
let fee_estimator = YourFeeEstimator();
Implementation notes:
- Fees must be returned in: satoshis per 1000 weight units
- Fees must be no smaller than 253 (equivalent to 1 satoshi/vbyte, rounded up)
- To reduce network traffic, you may want to cache fee results rather than retrieving fresh ones every time
Dependencies: none
References: Rust FeeEstimator
docs (opens new window), Java/Kotlin FeeEstimator
bindings (opens new window)
# Initialize the Router
What it's used for: Finds a Route for a payment between the given payer and a payee.
- Rust
- Kotlin
- Swift
let router = DefaultRouter::new(
network_graph.clone(),
logger.clone(),
keys_manager.get_secure_random_bytes(),
scorer.clone(),
ProbabilisticScoringFeeParameters::default()
)
Dependencies: P2PGossipSync
, Logger
, KeysManager
, Scorer
References: Rust Router
docs (opens new window), Java/Kotlin Router
bindings (opens new window)
# Initialize the Logger
What it's used for: LDK logging
- Rust
- Kotlin
- Swift
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
);
// <insert code to print this log and/or write this log to disk>
}
}
let logger = YourLogger();
Implementation notes: you'll likely want to write the logs to a file for debugging purposes.
Dependencies: none
References: Rust Logger
docs (opens new window), Java/Kotlin Logger
bindings (opens new window)
# Initialize the BroadcasterInterface
What it's used for: broadcasting various transactions to the bitcoin network
- Rust
- Kotlin
- Swift
struct YourTxBroadcaster();
impl BroadcasterInterface for YourTxBroadcaster {
fn broadcast_transactions(&self, txs: &[&Transaction]) {
// <insert code to broadcast a list of transactions>
}
}
let broadcaster = YourTxBroadcaster();
Dependencies: none
References: Rust BroadcasterInterface
docs (opens new window), Java/Kotlin BroadcasterInterface
bindings (opens new window)
# Initialize Persist
What it's used for: persisting ChannelMonitor
s, which contain crucial channel data, in a timely manner
- Rust
- Kotlin
- Swift
struct YourPersister();
impl<ChannelSigner: Sign> Persist for YourPersister {
fn persist_new_channel(
&self, id: OutPoint, data: &ChannelMonitor<ChannelSigner>
) -> Result<(), ChannelMonitorUpdateErr> {
// <insert code to persist the ChannelMonitor to disk and/or backups>
// Note that monitor.encode() will get you the ChannelMonitor as a
// Vec<u8>.
}
fn update_persisted_channel(
&self,
id: OutPoint,
update: &ChannelMonitorUpdate,
data: &ChannelMonitor<ChannelSigner>
) -> Result<(), ChannelMonitorUpdateErr> {
// <insert code to persist either the ChannelMonitor or the
// ChannelMonitorUpdate to disk>
}
}
let persister = YourPersister();
- Using LDK Sample Filesystem Persistence Crate in Rust
use lightning_persister::FilesystemPersister; // import LDK sample persist crate
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 oneChannelMonitor
per channel. They are persisted in real-time and thePersist
methods will block progress on sending or receiving payments until they return. You must ensure thatChannelMonitor
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 (opens new window), Java/Kotlin Persister
bindings (opens new window)
# Start Background Processing
What it's used for: running tasks periodically in the background to keep LDK operational.
- Rust
let background_processor = BackgroundProcessor::start(
persister,
Arc::clone(&invoice_payer),
Arc::clone(&chain_monitor),
Arc::clone(&channel_manager),
Arc::clone(&net_graph_msg_handler),
Arc::clone(&peer_manager),
Arc::clone(&logger),
);
Dependencies: ChannelManager
, ChainMonitor
, PeerManager
, Logger
References: Rust BackgroundProcessor::Start
docs (opens new window)
# Regularly Broadcast Node Announcement
What it's used for: if you have 1 or more public channels, you may need to announce your node and its channels occasionally. LDK will automatically announce channels when they are created, but there are no guarantees you have connected peers at that time or that your peers will propagate such announcements. The broader node-announcement message is not automatically broadcast.
- Rust
let mut interval = tokio::time::interval(Duration::from_secs(60));
loop {
interval.tick().await;
channel_manager.broadcast_node_announcement(
[0; 3], // insert your node's RGB color
node_alias,
vec![ldk_announced_listen_addr],
);
}
Dependencies: Peer Manager
References: PeerManager::broadcast_node_announcement
docs (opens new window)
# 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.
- Rust
- Kotlin
- Swift
struct YourTxFilter();
impl Filter for YourTxFilter {
fn register_tx(&self, txid: &Txid, script_pubkey: &Script) {
// <insert code for you to watch for this transaction on-chain>
}
fn register_output(&self, output: WatchedOutput) ->
Option<(usize, Transaction)> {
// <insert code for you to watch for any transactions that spend this
// output on-chain>
}
}
let filter = YourTxFilter();
Implementation notes: see the Blockchain Data guide for more info
Dependencies: none
References: Rust Filter
docs (opens new window), Java/Kotlin Filter
bindings (opens new window)
# 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
- Kotlin
- Swift
let filter: Option<Box<dyn Filter>> = // leave this as None or insert the Filter trait object
let chain_monitor = ChainMonitor::new(filter, &broadcaster, &logger, &fee_estimator, &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 (opens new window), Java/Kotlin ChainMonitor
bindings (opens new window)
# Initialize the KeysManager
What it's used for: providing keys for signing Lightning transactions
- Rust
- Kotlin
- Swift
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());
Implementation notes:
- See the Key Management guide for more info
- Note that you must write the
key_seed
you give to theKeysManager
on startup to disk, and keep using it to initialize theKeysManager
every time you restart. Thiskey_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 (opens new window), Java/Kotlin KeysManager
bindings (opens new window)
# 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
- Kotlin
- Swift
// Use LDK's sample persister crate 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.
for chan_mon in channel_monitors.iter() {
chan_mon.load_outputs_to_watch(&filter);
}
Dependencies: KeysManager
References: Rust load_outputs_to_watch
docs (opens new window)
# Initialize the ChannelManager
What it's used for: managing channel state
- Rust
- Kotlin
- Swift
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.
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,
&router,
&logger,
&entropy_source,
&node_signer,
&signer_provider,
user_config,
chain_params,
current_timestamp
);
(best_blockhash, fresh_channel_manager)
};
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
References: Rust ChannelManager
docs (opens new window), Java/Kotlin ChannelManager
bindings (opens new window)
# 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
- Kotlin
- Swift
// 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<u32>,
) -> AsyncBlockSourceResult<'a, BlockHeaderData> {
// <insert code to retrieve the header corresponding to header_hash>
}
fn get_block<'a>(
&'a mut self, header_hash: &'a BlockHash,
) -> AsyncBlockSourceResult<'a, Block> {
// <insert code to retrieve the block corresponding to header_hash>
}
fn get_best_block<'a>(&'a mut self) ->
AsyncBlockSourceResult<(BlockHash, Option<u32>)> {
// <insert code to retrieve your best-known block hash and height>
}
}
let block_source = YourChainBackend::new();
let mut chain_listener_channel_monitors = Vec::new();
let mut cache = UnboundedCache::new();
let mut chain_tip: Option<poll::ValidatedBlockHeader> = 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(),
);
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
crate as in the example above: the high-level steps that must be done for both ChannelManager
and each ChannelMonitor
are as follows:
- Get the last blockhash that each object saw.
- Receive the latest block hash when through deserializtion (opens new window) of the
ChannelManager
viaread()
- Each
ChannelMonitor
's is inchannel_manager.channel_monitors
, as the 2nd element in each tuple
- Receive the latest block hash when through deserializtion (opens new window) of the
- 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(..)
orchannel_monitor.block_disconnected(..)
until you reach the last common ancestor with the main chain. - 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/orchannel_monitor.block_connected(..)
. - Call
channel_manager.chain_sync_completed(..)
to complete the initial sync process.
Electrum/Esplora
Alternatively, you can use LDK's lightning-transaction-sync
crate. This provides utilities for syncing LDK via the transaction-based Confirm
interface.
# 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
- Kotlin
- Swift
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::<Arc<dyn chain::Access + Send + Sync>>,
logger.clone(),
));
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
. Network options include: Mainnet
,Regtest
,Testnet
,Signet
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 (opens new window), Access
docs (opens new window), Java/Kotlin P2PGossipSync
bindings (opens new window), Rust RapidGossipSync
docs (opens new window), Java/Kotlin RapidGossipSync
bindings (opens new window)
# Optional: Initialize Probabilistic Scorer
What it's used for: to find a suitable payment path to reach the destination.
- Rust
- Kotlin
- Swift
let network_graph_path = format!("{}/network_graph", ldk_data_dir.clone());
let network_graph = Arc::new(disk::read_network(Path::new(&network_graph_path), args.network, logger.clone()));
let scorer_path = format!("{}/scorer", ldk_data_dir.clone());
let scorer = Arc::new(RwLock::new(disk::read_scorer(
Path::new(&scorer_path),
Arc::clone(&network_graph),
Arc::clone(&logger),
)));
Dependencies: NetworkGraph
References: Rust ProbabilisticScorer
docs (opens new window), Java/Kotlin ProbabilisticScorer
docs (opens new window)