funder: cleanup datastore on state-change/channel failure

Let's not leave old state hanging around! Note that this fires
for pretty much every/any channel (even if we're not the opener).
This commit is contained in:
niftynei
2022-10-07 15:37:06 -05:00
committed by Christian Decker
parent efd096dc96
commit e00857827f
2 changed files with 98 additions and 1 deletions

View File

@@ -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)

View File

@@ -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!