diff --git a/plugins/funder.c b/plugins/funder.c index 5691b2d4293b..9707f1fe1bb2 100644 --- a/plugins/funder.c +++ b/plugins/funder.c @@ -128,6 +128,31 @@ command_hook_cont_psbt(struct command *cmd, struct wally_psbt *psbt) return command_finished(cmd, response); } +static struct command_result * +datastore_del_fail(struct command *cmd, + const char *buf, + const jsmntok_t *error, + void *data UNUSED) +{ + /* Eh, ok fine */ + return notification_handled(cmd); +} + +static struct command_result * +datastore_del_success(struct command *cmd, + const char *buf, + const jsmntok_t *result, + void *data UNUSED) +{ + /* Cool we deleted some stuff */ + plugin_log(cmd->plugin, LOG_DBG, + "`datastore` del succeeded: %*.s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + return notification_handled(cmd); +} + static struct command_result * datastore_add_fail(struct command *cmd, const char *buf, @@ -1047,6 +1072,64 @@ static struct command_result *json_disconnect(struct command *cmd, return notification_handled(cmd); } +static struct command_result * +delete_channel_from_datastore(struct command *cmd, + struct channel_id *cid) +{ + const struct out_req *req; + + /* Fetch out previous utxos from the datastore. + * If we were clever, we'd have some way of tracking + * channels that we actually might have data for + * but this is much easier */ + req = jsonrpc_request_start(cmd->plugin, cmd, + "deldatastore", + &datastore_del_success, + &datastore_del_fail, + NULL); + json_add_string(req->js, "key", + tal_fmt(cmd, "funder/%s", + type_to_string(cmd, struct channel_id, cid))); + return send_outreq(cmd->plugin, req); +} + +static struct command_result *json_channel_state_changed(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct channel_id cid; + const char *err, *old_state, *new_state; + + err = json_scan(tmpctx, buf, params, + "{channel_state_changed:" + "{channel_id:%" + ",old_state:%" + ",new_state:%}}", + JSON_SCAN(json_to_channel_id, &cid), + JSON_SCAN_TAL(cmd, json_strdup, &old_state), + JSON_SCAN_TAL(cmd, json_strdup, &new_state)); + + if (err) + plugin_err(cmd->plugin, + "`channel_state_changed` notification payload did" + " not scan %s: %.*s", + err, json_tok_full_len(params), + json_tok_full(buf, params)); + + /* Moving out of "awaiting lockin", + * means we clean up the datastore */ + /* FIXME: splicing state? */ + if (!streq(old_state, "DUALOPEND_AWAITING_LOCKIN") + && !streq(old_state, "CHANNELD_AWAITING_LOCKIN")) + return notification_handled(cmd); + + plugin_log(cmd->plugin, LOG_DBG, + "Cleaning up datastore for channel_id %s", + type_to_string(tmpctx, struct channel_id, &cid)); + + return delete_channel_from_datastore(cmd, &cid); +} + static struct command_result *json_channel_open_failed(struct command *cmd, const char *buf, const jsmntok_t *params) @@ -1074,7 +1157,8 @@ static struct command_result *json_channel_open_failed(struct command *cmd, if (open) unreserve_psbt(open); - return notification_handled(cmd); + /* Also clean up datastore for this channel */ + return delete_channel_from_datastore(cmd, &cid); } static void json_add_policy(struct json_stream *stream, @@ -1427,6 +1511,10 @@ const struct plugin_notification notifs[] = { "disconnect", json_disconnect, }, + { + "channel_state_changed", + json_channel_state_changed, + }, }; static char *option_channel_base(const char *arg, struct funder_policy *policy) diff --git a/tests/test_opening.py b/tests/test_opening.py index bdf4caae181a..fa02825714ac 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -398,14 +398,23 @@ def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams): funding_feerate=next_feerate) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) assert update['commitments_secured'] + # Sign our inputs, and continue signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] l1.rpc.openchannel_signed(chan_id, signed_psbt) + # There's data in the datastore now (l2 only) + assert l1.rpc.listdatastore() == {'datastore': []} + only_one(l2.rpc.listdatastore("funder/{}".format(chan_id))['datastore']) + # what happens when the channel opens? bitcoind.generate_block(6) l1.daemon.wait_for_log('to CHANNELD_NORMAL') + # Datastore should be cleaned up! + assert l1.rpc.listdatastore() == {'datastore': []} + assert l2.rpc.listdatastore() == {'datastore': []} + # This should be the accepter's amount fundings = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['funding'] # The lease is still there!