From 7733c2b0f490d53b24516dd88d2a23dba9024010 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 5 Oct 2022 14:23:42 -0500 Subject: [PATCH] funder: pull out previous input list from datastore on RBF It'd be nice to know which utxos we used previously, so we can rebuild a transaction using them! --- plugins/funder.c | 116 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 9 deletions(-) diff --git a/plugins/funder.c b/plugins/funder.c index 45e2be2f7..ff9ca01cc 100644 --- a/plugins/funder.c +++ b/plugins/funder.c @@ -361,6 +361,9 @@ struct open_info { u32 node_blockheight; struct amount_sat requested_lease; + + /* List of previously-used utxos */ + struct bitcoin_outpoint **prev_outs; }; static struct open_info *new_open_info(const tal_t *ctx) @@ -372,6 +375,7 @@ static struct open_info *new_open_info(const tal_t *ctx) info->requested_lease = AMOUNT_SAT(0); info->lease_blockheight = 0; info->node_blockheight = 0; + info->prev_outs = NULL; return info; } @@ -612,7 +616,7 @@ json_openchannel2_call(struct command *cmd, const char *buf, const jsmntok_t *params) { - struct open_info *info = tal(cmd, struct open_info); + struct open_info *info = new_open_info(cmd); struct amount_msat max_htlc_inflight, htlc_minimum; u64 commitment_feerate_perkw, feerate_our_max, feerate_our_min; @@ -739,6 +743,99 @@ json_openchannel2_call(struct command *cmd, return send_outreq(cmd->plugin, req); } +static struct command_result * +datastore_list_fail(struct command *cmd, + const char *buf, + const jsmntok_t *error, + struct open_info *info) +{ + struct out_req *req; + + /* Oops, something's broken */ + plugin_log(cmd->plugin, LOG_BROKEN, + "`datastore` list failed: %*.s", + json_tok_full_len(error), + json_tok_full(buf, error)); + + /* Figure out what our funds are... same flow + * as with openchannel2 callback. */ + req = jsonrpc_request_start(cmd->plugin, cmd, + "listfunds", + &listfunds_success, + &listfunds_failed, + info); + return send_outreq(cmd->plugin, req); +} + +static struct command_result * +datastore_list_success(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct open_info *info) +{ + struct out_req *req; + const char *key, *err; + const u8 *utxos_bin; + size_t len, i; + const jsmntok_t *ds_arr_tok, *ds_result; + + ds_arr_tok = json_get_member(buf, result, "datastore"); + assert(ds_arr_tok->type == JSMN_ARRAY); + + /* There should only be one result */ + utxos_bin = NULL; + json_for_each_arr(i, ds_result, ds_arr_tok) { + err = json_scan(tmpctx, buf, ds_result, + "{key:%,hex:%}", + JSON_SCAN_TAL(cmd, json_strdup, &key), + JSON_SCAN_TAL(cmd, json_tok_bin_from_hex, + &utxos_bin)); + + if (err) + plugin_err(cmd->plugin, + "`listdatastore` payload did" + " not scan. %s: %*.s", + err, json_tok_full_len(result), + json_tok_full(buf, result)); + + /* We found the prev utxo list */ + plugin_log(cmd->plugin, LOG_DBG, + "Saved utxos for channel (%s)" + " pulled from datastore", key); + + /* There should only be one result */ + break; + } + + /* Resurrect outpoints from stashed binary */ + len = tal_bytelen(utxos_bin); + while (len > 0) { + struct bitcoin_outpoint *outpoint = + tal(info, struct bitcoin_outpoint); + fromwire_bitcoin_outpoint(&utxos_bin, + &len, outpoint); + /* Cursor gets set to null if above fails */ + if (!utxos_bin) + plugin_err(cmd->plugin, + "Unable to parse saved utxos: %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + if (!info->prev_outs) + info->prev_outs = + tal_arr(info, struct bitcoin_outpoint *, 0); + + tal_arr_expand(&info->prev_outs, outpoint); + } + + req = jsonrpc_request_start(cmd->plugin, cmd, + "listfunds", + &listfunds_success, + &listfunds_failed, + info); + return send_outreq(cmd->plugin, req); +} + /* Peer has asked us to RBF */ static struct command_result * json_rbf_channel_call(struct command *cmd, @@ -747,7 +844,7 @@ json_rbf_channel_call(struct command *cmd, { struct open_info *info = new_open_info(cmd); u64 feerate_our_max, feerate_our_min; - const char *err; + const char *err, *chan_key; struct out_req *req; info->their_last_funding = tal(info, struct amount_sat); @@ -805,15 +902,16 @@ json_rbf_channel_call(struct command *cmd, return command_hook_success(cmd); } - /* Figure out what our funds are... same flow - * as with openchannel2 callback. We assume that THEY - * will use the same inputs, so we use whatever we want here */ + /* Fetch out previous utxos from the datastore */ req = jsonrpc_request_start(cmd->plugin, cmd, - "listfunds", - &listfunds_success, - &listfunds_failed, + "listdatastore", + &datastore_list_success, + &datastore_list_fail, info); - + chan_key = tal_fmt(cmd, "funder/%s", + type_to_string(cmd, struct channel_id, + &info->cid)); + json_add_string(req->js, "key", chan_key); return send_outreq(cmd->plugin, req); }