So long old friend, and thanks for all the demos!
11 KiB
Architecture of the eclair codebase
Eclair is developed in Scala and relies heavily on Akka. Akka is an actor programming framework similar to Erlang for the JVM.
The actor model provides a clean separation between components, allowing eclair to:
- Isolate faults and ensure high availability
- Scale across CPUs and machines efficiently
- Simplify development and testing
At a high-level, almost every entity is a separate, sandboxed actor:
- Every peer connection is an actor instance
- Every lightning channel is an actor instance
- Every payment attempt is an actor instance
Some actors are long-lived (e.g. lightning channels) while others are very short-lived (e.g. payment attempts).
Top-level projects
Eclair is split into three top-level projects:
eclair-core
: core library implementing lightningeclair-node
: server daemon built uponeclair-core
(exposes a Json RPC and WebSocket endpoint)eclair-front
: when using cluster mode, front-end server daemons handling peer connections
The entry point for eclair-core
is in Setup.scala
, where we start the actor system, connect to bitcoind
and create top-level actors.
Actor system overview
Here is a high-level view of the hierarchy of some of the main actors in the system:
+---------+
+-------->| Channel |
| +---------+
+------+ +---------+
+---------------->| Peer |----->| Channel |
| +------+ +---------+
| | +---------+
| +-------->| Channel |
| +---------+
+-------------+
| Switchboard |
+-------------+
| +---------+
| +-------->| Channel |
| | +---------+
| +------+ +---------+
+---------------->| Peer |----->| Channel |
+------+ +---------+
| +---------+
+-------->| Channel |
+---------+
+----------------+
+---------------->| ChannelRelayer |
| +----------------+
+---------+
| Relayer |
+---------+
| +-------------+
+---------------->| NodeRelayer |
+-------------+
+------------------+
+----------->| PaymentLifecycle |
| +------------------+
+---------------------------+ +------------------+
+---------------->| MultiPartPaymentLifecycle |----->| PaymentLifecycle |
| +---------------------------+ +------------------+
| | +------------------+
| +----------->| PaymentLifecycle |
+------------------+ +------------------+
| PaymentInitiator |
+------------------+ +------------------+
| +----------->| PaymentLifecycle |
| | +------------------+
| +---------------------------+ +------------------+
+---------------->| MultiPartPaymentLifecycle |----->| PaymentLifecycle |
+---------------------------+ +------------------+
| +------------------+
+----------->| PaymentLifecycle |
+------------------+
+---------------------+
+---------------->| MultiPartPaymentFSM |
| +---------------------+
+----------------+
| PaymentHandler |
+----------------+
| +---------------------+
+---------------->| MultiPartPaymentFSM |
+---------------------+
+----------+
| Register |
+----------+
+--------+
| Router |
+--------+
And a short description of each actor's role:
- Switchboard: creates and deletes peers
- Peer: p2p connection to another lightning node (standard lightning messages described in Bolt 1)
- Channel: channel with another lightning node (Bolt 2)
- Register: maps channel IDs to actors (provides a clean boundary between channel and payment components)
- PaymentInitiator: entry point for sending payments
- Relayer: entry point for relaying payments
- PaymentHandler: entry point for receiving payments
- Router: p2p gossip and the network graph (Bolt 7)
Actors have two ways of communicating:
- direct messages: when actors have a reference to other actors, they can exchange direct messages
- events: actors can emit events to a shared event stream, and other actors can register to these events
Payment scenarios
Let's dive into a few payment scenarios to show which actors are involved.
Sending a payment
When we send a payment:
- we run a path-finding algorithm (
Router
) - we split the payment into smaller chunks if MPP is used (
MultiPartPaymentLifecycle
) - we retry with alternative routes in some failure cases and record failing channels/payments (
PaymentLifecycle
) - we add HTLCs to some of our channels
+------------------+ +---------+
+----->| PaymentLifecycle |-----+ +----->| Channel |
| +------------------+ | | +---------+
+------------------+ +---------------------------+ | +------------------+ | +----------+ | +---------+
| PaymentInitiator |-------->| MultiPartPaymentLifecycle |----+----->| PaymentLifecycle |-----+----->| Register |-----+----->| Channel |
+------------------+ +---------------------------+ | +------------------+ | +----------+ | +---------+
| | +------------------+ | | +---------+
| +----->| PaymentLifecycle |-----+ +----->| Channel |
| +------------------+ +---------+
| |
| |
| +--------+ |
+----->| Router |<-----------+
+--------+
Receiving a payment
When we receive a payment:
- htlcs are forwarded by channels to the relayer
- a payment handler compares these htlcs to our payments database
- and decides to fail or fulfill them
+---------+
| Channel |-----+
+---------+ |
+---------+ | +---------+ +----------------+ +----------+
| Channel |-----+----->| Relayer |----->| PaymentHandler |----->| Register |
+---------+ | +---------+ +----------------+ +----------+
+---------+ |
| Channel |-----+
+---------+
Relaying a payment
When we relay a payment:
- htlcs are forwarded by channels to the relayer
- the relayer identifies the type of relay requested and delegates work to a channel relayer or a node relayer
- if a node relayer is used (trampoline payments):
- incoming htlcs are validated by a payment handler (similar to the flow to receive payments)
- outgoing htlcs are sent out (similar to the flow to send payments)
+----------------+ +----------+ +---------+
+--------->| ChannelRelayer |----->| Register |----->| Channel |
| +----------------+ +----------+ +---------+
+---------+ +---------+
| Channel |----->| Relayer |
+---------+ +---------+
| +-------------+ +---------------------------+
+--------->| NodeRelayer |----->| MultiPartPaymentLifecycle |
+-------------+ +---------------------------+
^
|
v
+----------------+
| PaymentHandler |
+----------------+
Channel scenarios
Let's describe some channel operations and see which actors are involved.
Opening a channel
When we open a channel:
- we exchange messages with our peer
- we use funds from our on-chain bitcoin wallet
- we start watching on-chain transactions to ensure our peer doesn't cheat us
+------+ +---------+ +--------+
| Peer |----->| Channel |-----+----->| Wallet |
+------+ +---------+ | +--------+
^ | | +---------+
| | +----->| Watcher |
+--------------+ +---------+
Closing a channel
When our peer tries to cheat:
- the blockchain watcher notices it and notifies the channel
- the channel publishes on-chain transactions
- and we notify our peer by sending an error message
+---------+ +---------+ +------+
| Watcher |<----->| Channel |----->| Peer |
+---------+ +---------+ +------+