-
Notifications
You must be signed in to change notification settings - Fork 952
Bookkeeper migration #8410
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bookkeeper migration #8410
Conversation
Cache cleared, restarting CI. |
Can't restart CI since it is too old, a rebase will start it again though. |
be18b01
to
f649a49
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checkpoint submission #1
I've done some more work:
I appended the fixes needed, for later rebasing. |
ef8cb22
to
5ab28b0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checkpoint #2
I will now rebase, since CI won't run until I do. Folded fixes so far, too... |
bd2e7a1
to
d9a1b37
Compare
3b297c0
to
c4d1280
Compare
…hot. This way we can be sure they're the same after migration. Signed-off-by: Rusty Russell <[email protected]>
Signed-off-by: Rusty Russell <[email protected]>
1245ed2
to
a75f409
Compare
@@ -1089,6 +1089,8 @@ static struct migration dbmigrations[] = { | |||
" fees BIGINT NOT NULL," | |||
" PRIMARY KEY (id)" | |||
")"), NULL}, | |||
/* We do a lookup before each append, to avoid duplicates */ | |||
{SQL("CREATE INDEX chain_moves_utxo_idx ON chain_moves (utxo)"), NULL}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An alternative would be to mark the utxo
as a unique index, which causes conflicts on insert, could save a roundtrip, but then we sort of assume none of our statements can fail, as that'd tear down the transaction.
@@ -3016,6 +3017,37 @@ void wallet_channel_close(struct wallet *w, | |||
db_bind_u64(stmt, channel_state_in_db(CLOSED)); | |||
db_bind_u64(stmt, chan->dbid); | |||
db_exec_prepared_v2(take(stmt)); | |||
|
|||
/* Update all accouting records to use channel_id string, instead of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
accouting
-> accounting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checkpoint #4
wallet/account_migration.c
Outdated
db_begin_transaction(account_db); | ||
/* Last migration was 24.08. Migrate there first if this happens. */ | ||
if (db_get_version(account_db) != 17) | ||
fatal("Cannot migrate account database version %u", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe suggest what version of CLN to use to update/migrate accounts.db to with first?
id = chain_mvt_index_created(ld, db, account, ev->credit, ev->debit); | ||
db_bind_u64(stmt, id); | ||
if (!mvt_tag_parse(ev->tag, strlen(ev->tag), &tag)) | ||
abort(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
print missing tag name for easier debugging?
@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Depends on sqlite3 database location") | ||
def test_bookkeeping_external_withdraw_missing(node_factory, bitcoind): | ||
""" Withdrawals to an external address turn up as | ||
extremely large onchain_fees when they happen before |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
taking this out, along with the run-recorder.c tests, means almost all of the explicit onchain fee behavior checks have been removed.
@@ -1616,7 +1513,7 @@ parse_and_log_chain_move(struct command *cmd, | |||
e->spending_txid = tal_steal(e, spending_txid); | |||
|
|||
/* Now try to get out the optional parts */ | |||
err = json_scan(tmpctx, buf, params, | |||
err = json_scan(tmpctx, buf, chainmove, | |||
"{coin_movement:" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you deleted this portion of the scan above (L1606) but left here.
static struct command_result *inject_done(struct command *notif_cmd, | ||
const char *methodname, | ||
const char *buf, | ||
const jsmntok_t *result, | ||
void *unused) | ||
{ | ||
return notification_handled(notif_cmd); | ||
/* We could do this lazily, but tests assume it happens now */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think there's only one test that relies on logs printed by the bookkeeper and that's the test_bookkeeping_descriptions test.
the other tests (should?) rely on the coin_movements.py plugin in tests (iirc)
e->debit = debit; | ||
e->timestamp = timestamp; | ||
e->tag = mvt_tag_str(tags[0]); | ||
plugin_log(cmd->plugin, LOG_DBG, "coin_move 2 (%s) %s -%s %s %"PRIu64, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if i'm reading the code correctly, there's only one test that relies on these (test_bookkeeping_descriptions) and you can probably transition over to using the internal RPCs to assert that the coin moves are being correctly picked up there?
# Send l2 funds via the channel | ||
l1.pay(l2, invoice_msat) | ||
l1.daemon.wait_for_log(r'coin movement:.*\'invoice\'') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you remove this, you can also get rid of L353, the coin_mvt_plugin
@@ -729,11 +648,15 @@ def test_bookkeeping_descriptions(node_factory, bitcoind, chainparams): | |||
# Send l2 funds via the channel | |||
bolt11_desc = 'test "bolt11" description, 🥰🪢' | |||
l1.pay(l2, 11000000, label=bolt11_desc) | |||
wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['htlcs'] == []) | |||
|
|||
# Need to call bookkeeper to trigger analysis! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these checks were added to make sure that the event completed. now that you've added the wait_for
above, you can remove them.
The new access APIs are more symmetrical: 1. edit_utxo_description -> add_utxo_description 2. add_payment_hash_desc -> add_payment_hash_description And to read it, instead of accessing ->ev_desc (now removed) we use chain_event_description() & channel_event_description(), threading bkpr though as needed. Signed-off-by: Rusty Russell <[email protected]>
Signed-off-by: Rusty Russell <[email protected]>
Remove the rebalance field from channel_event, and use the find_rebalance(bkpr, ev->db_id) to look it up instead. chain_event's also had a `rebalance` field, but it was only ever set (to false), never read. Note: list_rebalances() was only used by tests, not a public API. Signed-off-by: Rusty Russell <[email protected]>
We want to access it in stmt2chain_event, so plumb it through. Signed-off-by: Rusty Russell <[email protected]>
We won't be able to "UPDATE chain_events", so keep a separate record of these blockheights, and lookup that when the blockheight is 0. Signed-off-by: Rusty Russell <[email protected]>
Python's assert gives great analysis of what the differences are, making debugging much easier. So feed it dicts, not tuples, and simply do an assert. Signed-off-by: Rusty Russell <[email protected]>
…njection. For the moment, we'll continue to use bookkeeper to monitor the notifications to insert these (we don't have the internal infrastructure for that, and actually these commands are probably better than using notifications). We hoist param_outpoint() into common code, since there are already two uses. Signed-off-by: Rusty Russell <[email protected]>
…njected. This allows the bookkeeper plugin to know it's not actually a channel account. Remove the "ignored" tag from the schema too: we removed it previously. Signed-off-by: Rusty Russell <[email protected]>
… transfer_from. Before bkpr_listaccountevents() gave entries with origin like: {'account': "nifty's secret stash", 'blockheight': 111, 'credit_msat': 180000000, 'currency': 'bcrt', 'debit_msat': 0, 'origin': 'null', 'outpoint': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:0', 'tag': 'deposit', 'timestamp': 1679955976, 'type': 'chain'}, Changelog-Changed: Plugins: "utxo_deposit" is allows to have missing `transfer_from`, and null is not considered an account name. Signed-off-by: Rusty Russell <[email protected]>
…xodeposit / injectutxospend calls. And thus we absorb them as normal when they come back as "foreign" entries. Signed-off-by: Rusty Russell <[email protected]>
After much thought and mis-steps, I chose a simple solution: open another fd for sync comms. It's almost impossible to know what state the async one is in. jsonrpc_request_sync() is enhanced to return a valid tal object, as the current behaviour of returning a pointer to inside an array was surprising. Changelog-Changed: libplugin: you can now call the synchronous API functions at any time (not just in the init callback). Signed-off-by: Rusty Russell <[email protected]>
It's a great test, but it's very hard to simulate now we are going to be going from the internal db. Signed-off-by: Rusty Russell <[email protected]>
Rearrange all the JSON interfaces to call refresh_moves() (async) before doing anything. This does nothing for now, but it will be useful once we transition from notifications to using the list commands. Signed-off-by: Rusty Russell <[email protected]>
This is reliable, meaning we should never get replayed events. We have to reference count to make sure all commands are complete, before we return. In particular, annotating with descriptions can involve several calls to list commands. We need to give them the results *after* this is all complete. test_bookkeeping_descriptions() relied on log messages from notifications, which now only happen when a command is called. This changes the test a bit. Since we no longer subscribe to the balance_snapshot event, we need to create the wallet account at initialization, as callers expect it to exist. Signed-off-by: Rusty Russell <[email protected]>
We don't need it now bookkeeper uses the list commands. Signed-off-by: Rusty Russell <[email protected]>
We're going to be using this instead of our internal db. I also made json_out_obj() take the str arg, as it didn't and I expected it to. Signed-off-by: Rusty Russell <[email protected]>
Cleaner (I'm about to hand it a sha256 on the stack). Signed-off-by: Rusty Russell <[email protected]>
With some help (and hinderance!) from ChatGPT: the field names differ slightly from our internal db. The particilar wrinkle is that we have to restrict all queries to limit them to entries we've seen already. Our code expects this (we used to only enter it into the db when we processed it), and it would otherwise be confusing if a sql query returned inconsistent results because an event occurred while bookkeeper was processing. Signed-off-by: Rusty Russell <[email protected]>
There will be no more missing events (and at initialization time, we will do that as a migration). Signed-off-by: Rusty Russell <[email protected]>
Changelog-Changed: Plugins: `bookkeeper` now uses the lightningd database, not "accounts.db". Signed-off-by: Rusty Russell <[email protected]>
Now handles when we remove the db. Signed-off-by: Rusty Russell <[email protected]>
And gracefully fail for this case. There's no such thing for Postgres, but that's because dbs need to be set up by the admin. Signed-off-by: Rusty Russell <[email protected]>
We take over the --bookkeeper-dir and --bookkeeper-db options, and then if we can find the bookkeeper db we extract the records to initialize our chain_moves and channel_moves tables. Of course, bookkeeper now needs to not register those options. When bookkeeper gets invoked the first time, it will reconstruct everything from listchannelmoves and listcoinmoves. It cannot preserve manually-added descriptions, so we put those in the datastore for it ready to go. Note that the order of onchain_fee changes slightly from the original. But this is fine. Signed-off-by: Rusty Russell <[email protected]>
If we don't have an accountdb from bookkeeper: 1. Generate a deposit chain event for every confirmed UTXO. 2. Generate an open chain event for every open, confirmed channel. 3. Generate a push/lease event if necessary. 4. Generate a fixup "journal" entry if balance is different from initial. Signed-off-by: Rusty Russell <[email protected]>
This requires us to turn "sql" calls into calls to a local db, which means pulling in a lot of infrastructure. But it's possible. Signed-off-by: Rusty Russell <[email protected]>
ubsan complains that we declared a function not to take NULL. Signed-off-by: Rusty Russell <[email protected]>
Signed-off-by: Rusty Russell <[email protected]>
32ed994
to
5a38d5c
Compare
94c1cf5
into
ElementsProject:master
This builds on #8445
We do all the things:
It needs some more testing, and I'm sure it dosn't pass CI, but it's ready for review!
Closes: #8393