cln/plugins/bkpr
Rusty Russell 86f459511f update-mocks: make handling of mocks in nested files deterministic.
update-mocks searches for prototypes, looking first in the local
directory, then in */*.h, and takes the first.  In the case of
plugins/bkpr/test/run-currencyrate_str.c referring to
jsonrpc_request_start_, this means it could get either the one in
libplugin (correct) or the one in lightningd (wrong!), depending on
directory order randomness.

Hack it a bit harder, to look one up from the local dir before */*.h.
This is redundant in most cases, but not for three-deep-nested dirs
like this.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2026-05-11 13:51:52 +09:30
..
test update-mocks: make handling of mocks in nested files deterministic. 2026-05-11 13:51:52 +09:30
account.c plugins: simplify datastore interfaces now callers have mkdatastorekey. 2026-03-23 14:03:17 +10:00
account.h ]bkpr: save accounts in the datastore instead of database. 2025-08-19 13:37:50 +09:30
account_entry.c bkpr: strings in structures should be const. 2025-08-19 13:37:50 +09:30
account_entry.h bkpr: strings in structures should be const. 2025-08-19 13:37:50 +09:30
blockheights.c plugins: simplify datastore interfaces now callers have mkdatastorekey. 2026-03-23 14:03:17 +10:00
blockheights.h bkpr: add in-mem & datastore storage for external blockheights. 2025-08-19 13:37:50 +09:30
bookkeeper.c common/bolt12: use a const char * for fail reason. 2026-05-11 11:13:55 +09:30
bookkeeper.h bookkeeper: implement flexible "bkpr-report" command. 2026-03-23 14:03:17 +10:00
chain_event.c bookeeper: add currencyrate to bkpr-listaccounts if we recorded it. 2026-03-23 14:03:17 +10:00
chain_event.h bkpr: forward utxo_deposit / utxo_spend notifications to new injectutxodeposit / injectutxospend calls. 2025-08-19 13:37:50 +09:30
channel_event.c bookeeper: add currencyrate to bkpr-listaccounts if we recorded it. 2026-03-23 14:03:17 +10:00
channel_event.h bkpr: take, don't steal in new_channel_event. 2025-08-19 13:37:50 +09:30
channelsapy.c common: add amount_msat_deduct / amount_msat_deduct_sub. 2025-11-17 10:56:18 +10:30
channelsapy.h bkpr: convert all the local db sql queries into calls to sql plugin. 2025-08-19 13:37:50 +09:30
descriptions.c plugins: simplify datastore interfaces now callers have mkdatastorekey. 2026-03-23 14:03:17 +10:00
descriptions.h bookkeeper: infrastructure to keep descriptions (annotations) in mem and datastore. 2025-08-19 13:37:50 +09:30
incomestmt.c bookkeeper: implement flexible "bkpr-report" command. 2026-03-23 14:03:17 +10:00
incomestmt.h bookkeeper: implement flexible "bkpr-report" command. 2026-03-23 14:03:17 +10:00
Makefile bookkeeper: implement flexible "bkpr-report" command. 2026-03-23 14:03:17 +10:00
onchain_fee.c plugins: simplify datastore interfaces now callers have mkdatastorekey. 2026-03-23 14:03:17 +10:00
onchain_fee.h bookeeper: add currencyrate to bkpr-listaccounts if we recorded it. 2026-03-23 14:03:17 +10:00
README.md chore: fix some minor issues 2025-08-15 11:05:51 +09:30
rebalances.c plugins: simplify datastore interfaces now callers have mkdatastorekey. 2026-03-23 14:03:17 +10:00
rebalances.h bkpr: infrastructure to keep rebalance pairs in mem & datastore. 2025-08-19 13:37:50 +09:30
recorder.c bkpr: limp along if we lost our db. 2026-02-17 12:10:26 +10:30
recorder.h bookkeeper: save last timestamp to avoid another query in find_consolidated_fees. 2025-10-21 20:21:56 +10:30
report.c bkpr-report: enhance tag syntax from {tag:replacement} to {tag?if-set:if-not-set} 2026-04-02 14:54:20 +10:00
report.h bookkeeper: implement flexible "bkpr-report" command. 2026-03-23 14:03:17 +10:00
sql.c bkpr: helpers to query sql plugin for chainmoves and channelmoves. 2025-08-19 13:37:50 +09:30
sql.h bkpr: restore run-recorder. 2025-08-19 13:37:50 +09:30

The bookkeeper keeps track of coins moving through your Lightning node.

See the doc/PLUGINS.md#coin_movement section on the message that CLN emits for us to process.

// FIXME: add more detailed documenation for how bookkeeper works.

3rd Party Coin Movements

Bookeeper ingests 3rd party plugin notifications about on-chain movements that it should watch.

This allows for us to account for non-internal on-chain wallets in the single place, making bookkeeper your single source of truth for bitcoin for an organization or node-operator.

As a plugin writer, if you want to emit onchain events that the bookkeeper should track, you should emit an event with the following format:

{
	"utxo_deposit": {
		"account": "nifty's secret stash",
		"transfer_from: null,
		"outpoint": xxxx:x,
		"amount_msat": "10000sat",
		"coin_type": "bc",
		"timestamp": xxxx,
		"blockheight": xxx,
	}
}
{
	"utxo_spend": {
		"account": "nifty's secret stash",
		"outpoint": xxxx:x,
		"spending_txid": xxxx,
		"amount_msat": "10000sat",
		"coin_type": "bc",
		"timestamp": xxxx,
		"blockheight": xxx,
	}
}

Withdrawing money (sending to a external account)

Sending money to an external account is a bit unintuitive in the UTXO model that we're using to track coin moves; technically a send to an external account is a "deposit" to 3rd party's UTXO.

To account for these, bookkeeper expects to receive a utxo_deposit event for the creation of an output to a 3rd party. It's assumed that you'll issue these at transaction creation time, and that they won't be final until we receive notice of spend of the inputs in the tx that created them.

To notify that money is being sent to a 3rd party output, here's the event we'd expect.

The two keys here are the following:

  • The account is external. This is a special account in bookkeeper and used for tracking external deposits (aka sends)
  • The transfer_from field is set to the name of the account that is sending out the money.
{
	"utxo_deposit": {
		"account": "external",
		"transfer_from": "nifty's secret stash",
		"outpoint": xxxx:x,
		"amount_msat": "10000sat",
		"coin_type": "bc",
		"timestamp": xxxx,
		"blockheight": xxx,
	}
}

List of todos

List of things to check/work on, as a todo list.

  • Transfers btw a 3rd party wallet and the internal CLN wallet? These should be registered as internal transfers and not show up in listincome