Skip to content

Bitcoin backend plugins planning #3354

@cdecker

Description

@cdecker

I've been planning to work on a plugin-based bitcoin backend for a while but
haven't gotten around to actually working on it. Since others are trying to
get started on this I thought I'd share some of my thoughts around how this
could be accomplished.

Current situation

Our current interactions with the bitcoin network is nicely encapsulated in
lightningd/bitcoin.c. From there we can see that the RPC methods we call are
the following:

  • estimatesmartfee used for fee estimation on a couple of different
    horizons (1, 3, 6 blocks).
  • getblock (raw and hex encoded) used to retrieve a block after calling
    getblockhash to get its hash given the height.
  • getblockhash to translate a height into a blockhash
  • gettxout to check whether a tx output is still unspent, i.e., in the UTXO.
  • getnetworkinfo used to check that bitcoind is a supported version only.
  • getblockchaininfo used to check if bitcoind is in IBD.
  • sendrawtransaction used to send a transaction to the network.

Backend Interface

I'd like to make the interface for backend a bit more coarse-grained, in order
to give the plugins more freedom as to how they accomplish their task. My go
to example is getblockhash + getblock which is always called in this order
and requires multiple roundtrips to bitcoind for a single logical operation.

Some of the methods above can be translated 1-to-1 into the plugin:

  • sendrawtransaction can become a simple hook, if only for the reason that
    we don't want to expose it to the JSON-RPC users. In addition with
    prioritized and chained hooks we can have a failover chain that tries one
    plugin after the other in case of failure.

Other methods can be combined into more abstract methods:

  • getblockbyheight is a combination of getblockhash and getblock which
    either returns the hash and the block at a given height or an error if
    there is no block (yet). In addition we could give a tiny hint as to the
    context in which the query is done (scan) to tell the plugin whether we
    are in a linear scan to catch up with the blockchain head. That'd allow the
    plugin to pre-fetch the next few blocks, effectively pipelining the sync.
  • getchaininfo can subsume the calls to getnetworkinfo and
    getblockchaininfo because they are mostly done on startup, and
    effectively contain the same information. A version number for the set of
    capabilities exposed by the backend plugin would still be in order to guard
    against using an outdated backend with a newer version that expects some
    things to be available.
  • getoutputbyscid is a combination of getblockhash, getblock
    (verbosity=1) and gettxout used when verifying a channel's outpoint
    from its announcement. This is one of the most costly calls we have since
    it threads through 3 RPC calls and is called for every channel we learn
    about (it's cached later, but on new nodes this is the heavy hitter). We
    now optimized this to use filtered blocks, but it's still in use by some
    parts of the machinery.
  • getfilteredblock is an alternative to getoutputbyscid issue. It gets
    the entire block, filters out transactions that are not interesting (don't
    have unspent outputs or don't have P2WSH outputs) and returns them to be
    processed. We would probably implement either this bulk interface or
    getoutputbyscid. The latter would allow plugins to implement something
    smart, but comes at the cost of more hook calls.

And finally we can make some things notifications:

  • fee could give a bulk update on all horizons we are interested in and
    push a fee update from the plugin to lightningd. That means we could also
    respond to things happening on the network that could affect fees (new
    block being found), or we could implement fee smoothing at the plugin level.
  • block and tx notifications could alert us about incoming blocks (header
    and height) or transactions we might be interested in. The latter would
    mean we need to tell the plugin about addresses and outputs we are
    interested in, but it'd be only an optimization anyway since we'll catch
    them during the regular processing of blocks.

Hook vs JSON-RPC passthrough

Most of the above methods could be implemented either as JSON-RPC methods
(with passthrough to the user RPC interface) or as hooks. JSON-RPC methods are
nice since they allow the user to query the same backend that lightningd
uses internally (facilitating debugging), however they may end up cluttering
the RPC interface with semi-related things. Hooks on the other hand are
internal only and with the planned chaining of hooks we can have multiple
plugins with different priorities providing transparent failover (something
that with JSON-RPC is not planned right now), the downside is that the
failover isn't implemented yet and the backend is not accessible for
debugging.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions