Skip to content

Commit

Permalink
funder: cleanup datastore on state-change/channel failure
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
niftynei committed Oct 11, 2022
1 parent 0cfbb5b commit 3a98aa4
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 1 deletion.
90 changes: 89 additions & 1 deletion plugins/funder.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions tests/test_opening.py
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down

0 comments on commit 3a98aa4

Please sign in to comment.