mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-12-29 10:04:41 +01:00
1146 lines
33 KiB
C
1146 lines
33 KiB
C
#include <assert.h>
|
|
#include <bitcoin/psbt.h>
|
|
#include <ccan/ccan/array_size/array_size.h>
|
|
#include <ccan/ccan/cast/cast.h>
|
|
#include <ccan/ccan/tal/str/str.h>
|
|
#include <common/json_stream.h>
|
|
#include <common/psbt_open.h>
|
|
#include <common/type_to_string.h>
|
|
#include <common/utils.h>
|
|
#include <plugins/spender/multifundchannel.h>
|
|
#include <plugins/spender/openchannel.h>
|
|
#include <wally_psbt.h>
|
|
|
|
static struct list_head mfc_commands;
|
|
|
|
static void
|
|
destroy_mfc(struct multifundchannel_command *mfc)
|
|
{
|
|
list_del(&mfc->list);
|
|
}
|
|
|
|
static void register_mfc(struct multifundchannel_command *mfc)
|
|
{
|
|
assert(mfc);
|
|
|
|
list_add_tail(&mfc_commands, &mfc->list);
|
|
tal_add_destructor(mfc, &destroy_mfc);
|
|
}
|
|
|
|
static struct multifundchannel_destination *
|
|
find_dest_by_channel_id(struct channel_id *cid)
|
|
{
|
|
struct multifundchannel_command *mfc, *n;
|
|
|
|
list_for_each_safe (&mfc_commands, mfc, n, list) {
|
|
for (size_t i = 0; i < tal_count(mfc->destinations); i++) {
|
|
struct multifundchannel_destination *dest =
|
|
&mfc->destinations[i];
|
|
if (channel_id_eq(&dest->channel_id, cid))
|
|
return dest;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct command_result *
|
|
redo_multiopenchannel(struct multifundchannel_command *mfc,
|
|
const char *failing_method)
|
|
{
|
|
// FIXME
|
|
plugin_err(mfc->cmd->plugin,
|
|
"REDO CALLED AT %s", failing_method);
|
|
return NULL;
|
|
}
|
|
|
|
/* There's a few ground rules here about how we store/keep
|
|
* the PSBT input/outputs in such a way that we can Do The
|
|
* Right Thing for each of our peers.
|
|
*
|
|
* c-lightning will make sure that our peer isn't removing/adding
|
|
* any updates that it's not allowed to (i.e. ours or a different
|
|
* node's that we're pretending are 'ours').
|
|
*
|
|
* The parent copy of the PSBT has all of the inputs/outputs added to it,
|
|
* but the serial_ids are decremented by one to the even-pair, e.g. a
|
|
* serial_id of 3 -> 2; 17 -> 16; etc.
|
|
*
|
|
* If the even-pair of a provided serial_id is taken/occupied,
|
|
* the update is rejected (the parent is unmodified and the
|
|
* function returns false).
|
|
*
|
|
* The peer's inputs/outputs updates are then copied to the parent psbt.
|
|
*/
|
|
static bool update_parent_psbt(const tal_t *ctx,
|
|
struct multifundchannel_destination *dest,
|
|
struct wally_psbt *old_node_psbt,
|
|
struct wally_psbt *new_node_psbt,
|
|
struct wally_psbt **parent_psbt)
|
|
{
|
|
struct psbt_changeset *changes;
|
|
struct wally_psbt *clone, *new_node_copy;
|
|
|
|
/* Clone the parent, so we don't make any changes to it
|
|
* until we've succesfully done everything */
|
|
|
|
/* Only failure is alloc, should we even check? */
|
|
tal_wally_start();
|
|
if (wally_psbt_clone_alloc(*parent_psbt, 0, &clone) != WALLY_OK)
|
|
abort();
|
|
tal_wally_end(tal_steal(ctx, clone));
|
|
|
|
/* This makes it such that we can reparent/steal added
|
|
* inputs/outputs without impacting the 'original'. We
|
|
* could avoid this if there was a 'wally_psbt_input_clone_into'
|
|
* function, or the like */
|
|
tal_wally_start();
|
|
if (wally_psbt_clone_alloc(new_node_psbt, 0, &new_node_copy)
|
|
!= WALLY_OK)
|
|
abort();
|
|
/* copy is cleaned up below */
|
|
tal_wally_end(NULL);
|
|
|
|
changes = psbt_get_changeset(NULL, old_node_psbt,
|
|
new_node_copy);
|
|
/* Inputs */
|
|
for (size_t i = 0; i < tal_count(changes->added_ins); i++) {
|
|
u64 serial;
|
|
int s_idx;
|
|
const struct wally_psbt_input *in =
|
|
&changes->added_ins[i].input;
|
|
size_t idx = clone->num_inputs;
|
|
|
|
if (!psbt_get_serial_id(&in->unknowns, &serial))
|
|
goto fail;
|
|
|
|
/* Ignore any input that's ours */
|
|
if (serial % 2 == TX_INITIATOR)
|
|
continue;
|
|
|
|
/* Check that serial does not exist on parent already */
|
|
s_idx = psbt_find_serial_input(clone, serial - 1);
|
|
if (s_idx != -1)
|
|
goto fail;
|
|
|
|
psbt_add_input(clone,
|
|
&changes->added_ins[i].tx_input,
|
|
idx);
|
|
/* Move the input over */
|
|
clone->inputs[idx] = *in;
|
|
|
|
/* Update the added serial on the clone to the correct
|
|
* position */
|
|
psbt_input_set_serial_id(clone, &clone->inputs[idx],
|
|
serial - 1);
|
|
}
|
|
|
|
for (size_t i = 0; i < tal_count(changes->rm_ins); i++) {
|
|
u64 serial;
|
|
int s_idx;
|
|
const struct wally_psbt_input *in =
|
|
&changes->rm_ins[i].input;
|
|
|
|
if (!psbt_get_serial_id(&in->unknowns, &serial))
|
|
goto fail;
|
|
|
|
/* If it's ours, that's a whoops */
|
|
if (serial % 2 == TX_INITIATOR)
|
|
goto fail;
|
|
|
|
/* Check that serial exists on parent already */
|
|
s_idx = psbt_find_serial_input(clone, serial - 1);
|
|
if (s_idx == -1)
|
|
goto fail;
|
|
|
|
/* Remove input */
|
|
if (wally_psbt_remove_input(clone, s_idx) != WALLY_OK)
|
|
goto fail;
|
|
}
|
|
|
|
/* Outputs */
|
|
for (size_t i = 0; i < tal_count(changes->added_outs); i++) {
|
|
u64 serial, parent_serial;
|
|
const struct wally_psbt_output *out =
|
|
&changes->added_outs[i].output;
|
|
int s_idx;
|
|
size_t idx = clone->num_outputs;
|
|
|
|
if (!psbt_get_serial_id(&out->unknowns, &serial))
|
|
goto fail;
|
|
|
|
if (serial % 2 == TX_INITIATOR) {
|
|
/* If it's the funding output, we add it */
|
|
if (serial == dest->funding_serial) {
|
|
parent_serial = dest->funding_serial;
|
|
} else
|
|
continue;
|
|
} else
|
|
parent_serial = serial - 1;
|
|
|
|
/* Check that serial does not exist on parent already */
|
|
s_idx = psbt_find_serial_output(clone, parent_serial);
|
|
if (s_idx != -1)
|
|
goto fail;
|
|
|
|
psbt_add_output(clone,
|
|
&changes->added_outs[i].tx_output,
|
|
idx);
|
|
/* Move output over */
|
|
clone->outputs[idx] = *out;
|
|
|
|
/* Update the added serial on the clone to the correct
|
|
* position */
|
|
psbt_output_set_serial_id(clone, &clone->outputs[idx],
|
|
parent_serial);
|
|
}
|
|
|
|
for (size_t i = 0; i < tal_count(changes->rm_outs); i++) {
|
|
u64 serial;
|
|
int s_idx;
|
|
const struct wally_psbt_output *out =
|
|
&changes->rm_outs[i].output;
|
|
|
|
if (!psbt_get_serial_id(&out->unknowns, &serial))
|
|
goto fail;
|
|
|
|
/* If it's ours, that's a whoops */
|
|
if (serial % 2 == TX_INITIATOR)
|
|
goto fail;
|
|
|
|
/* Check that serial exists on parent already */
|
|
s_idx = psbt_find_serial_output(clone, serial - 1);
|
|
if (s_idx == -1)
|
|
goto fail;
|
|
|
|
/* Remove output */
|
|
if (wally_psbt_remove_output(clone, s_idx) != WALLY_OK)
|
|
goto fail;
|
|
}
|
|
|
|
/* We want to preserve the memory bits associated with
|
|
* the inputs/outputs we just copied over when we free
|
|
* the copy, so remove ones the *added* from the copy.
|
|
* We go from the back since this will modify the indexes */
|
|
for (size_t i = tal_count(changes->added_ins) - 1;
|
|
i > -1;
|
|
i--) {
|
|
psbt_rm_input(new_node_copy,
|
|
changes->added_ins[i].idx);
|
|
}
|
|
for (size_t i = tal_count(changes->added_outs) - 1;
|
|
i > -1;
|
|
i--) {
|
|
psbt_rm_output(new_node_copy,
|
|
changes->added_outs[i].idx);
|
|
}
|
|
|
|
tal_free(changes);
|
|
tal_free(new_node_copy);
|
|
|
|
tal_free(*parent_psbt);
|
|
*parent_psbt = clone;
|
|
return true;
|
|
|
|
fail:
|
|
tal_free(changes);
|
|
tal_free(new_node_copy);
|
|
tal_free(clone);
|
|
return false;
|
|
}
|
|
|
|
/* After all of the changes have been applied to the parent psbt,
|
|
* we update each node_psbt with the changes from every *other* peer.
|
|
*
|
|
* This updated node_psbt is the one that we should pass to
|
|
* openchannel_update for the next round.
|
|
*
|
|
* We do update rounds until every peer returns "commitment_secured:true",
|
|
* which will happen at the same round (as it requires us passing in
|
|
* an identical PSBT as the previous round).
|
|
*/
|
|
static bool update_node_psbt(const tal_t *ctx,
|
|
const struct wally_psbt *parent_psbt,
|
|
struct wally_psbt **node_psbt)
|
|
{
|
|
/* How to update this? We could do a comparison.
|
|
* More easily, we simply clone the parent and update
|
|
* the correct serial_ids for the node_psbt */
|
|
struct wally_psbt *clone;
|
|
|
|
tal_wally_start();
|
|
/* Only failure is alloc */
|
|
if (wally_psbt_clone_alloc(parent_psbt, 0, &clone) != WALLY_OK)
|
|
abort();
|
|
tal_wally_end(tal_steal(ctx, clone));
|
|
|
|
/* For every peer's input/output, flip the serial id
|
|
* on the clone. They should all be present. */
|
|
for (size_t i = 0; i < (*node_psbt)->num_inputs; i++) {
|
|
u64 serial_id;
|
|
int input_index;
|
|
if (!psbt_get_serial_id(&(*node_psbt)->inputs[i].unknowns,
|
|
&serial_id)) {
|
|
tal_wally_end(tal_free(clone));
|
|
return false;
|
|
}
|
|
|
|
/* We're the initiator here. If it's not the peer's
|
|
* input, skip it */
|
|
if (serial_id % 2 == TX_INITIATOR)
|
|
continue;
|
|
/* Down one, as that's where it'll be on the parent */
|
|
input_index = psbt_find_serial_input(clone, serial_id - 1);
|
|
/* Must exist */
|
|
assert(input_index != -1);
|
|
/* Update the cloned input serial to match the node's
|
|
* view */
|
|
psbt_input_set_serial_id(clone, &clone->inputs[input_index],
|
|
serial_id);
|
|
|
|
}
|
|
|
|
for (size_t i = 0; i < (*node_psbt)->num_outputs; i++) {
|
|
u64 serial_id;
|
|
int output_index;
|
|
if (!psbt_get_serial_id(&(*node_psbt)->outputs[i].unknowns,
|
|
&serial_id)) {
|
|
tal_wally_end(tal_free(clone));
|
|
return false;
|
|
}
|
|
/* We're the initiator here. If it's not the peer's
|
|
* output, skip it */
|
|
if (serial_id % 2 == TX_INITIATOR)
|
|
continue;
|
|
|
|
/* Down one, as that's where it'll be on the parent */
|
|
output_index = psbt_find_serial_output(clone,
|
|
serial_id - 1);
|
|
/* Must exist */
|
|
assert(output_index != -1);
|
|
|
|
/* Update the cloned input serial to match the node's
|
|
* view */
|
|
psbt_output_set_serial_id(clone, &clone->outputs[output_index],
|
|
serial_id);
|
|
|
|
}
|
|
|
|
tal_free(*node_psbt);
|
|
*node_psbt = clone;
|
|
return true;
|
|
}
|
|
|
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*
|
|
* To collect signatures from our peers, we use a notification watcher.
|
|
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*
|
|
*/
|
|
static struct command_result *
|
|
openchannel_finished(struct multifundchannel_command *mfc)
|
|
{
|
|
struct json_stream *out;
|
|
|
|
plugin_log(mfc->cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64": done.", mfc->id);
|
|
|
|
out = jsonrpc_stream_success(mfc->cmd);
|
|
json_add_string(out, "tx", mfc->final_tx);
|
|
json_add_string(out, "txid", mfc->final_txid);
|
|
json_array_start(out, "channel_ids");
|
|
for (size_t i = 0; i < tal_count(mfc->destinations); i++) {
|
|
struct multifundchannel_destination *dest =
|
|
&mfc->destinations[i];
|
|
|
|
json_object_start(out, NULL);
|
|
json_add_node_id(out, "id", &dest->id);
|
|
json_add_channel_id(out, "channel_id", &dest->channel_id);
|
|
json_add_num(out, "outnum", dest->outnum);
|
|
json_object_end(out);
|
|
}
|
|
json_array_end(out);
|
|
|
|
json_array_start(out, "failed");
|
|
for (size_t i = 0; i < tal_count(mfc->removeds); i++) {
|
|
struct multifundchannel_removed *rm =
|
|
&mfc->removeds[i];
|
|
json_object_start(out, NULL);
|
|
json_add_node_id(out, "id", &rm->id);
|
|
json_add_string(out, "method", rm->method);
|
|
json_add_jsonstr(out, "error", rm->error);
|
|
json_object_end(out);
|
|
}
|
|
json_array_end(out);
|
|
|
|
mfc->psbt = tal_free(mfc->psbt);
|
|
return mfc_finished(mfc, out);
|
|
}
|
|
|
|
static struct command_result *
|
|
after_openchannel_signed(struct multifundchannel_command *mfc)
|
|
{
|
|
--mfc->pending;
|
|
if (mfc->pending == 0)
|
|
return openchannel_finished(mfc);
|
|
else
|
|
return command_still_pending(mfc->cmd);
|
|
}
|
|
|
|
static struct command_result *
|
|
openchannel_signed_ok(struct command *cmd,
|
|
const char *buf,
|
|
const jsmntok_t *result,
|
|
struct multifundchannel_destination *dest)
|
|
{
|
|
struct multifundchannel_command *mfc = dest->mfc;
|
|
|
|
plugin_log(mfc->cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64", dest %u: `openchannel_signed` done",
|
|
mfc->id, dest->index);
|
|
|
|
/* One of the other commands might have landed here first */
|
|
if (!mfc->final_tx) {
|
|
const jsmntok_t *tx_tok, *txid_tok;
|
|
|
|
tx_tok = json_get_member(buf, result, "tx");
|
|
if (!tx_tok)
|
|
plugin_err(mfc->cmd->plugin,
|
|
"`openchannel_signed` has no 'tx': %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
|
|
mfc->final_tx = json_strdup(mfc, buf, tx_tok);
|
|
|
|
txid_tok = json_get_member(buf, result, "txid");
|
|
if (!txid_tok)
|
|
plugin_err(mfc->cmd->plugin,
|
|
"`openchannel_signed` has no 'txid': %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
|
|
mfc->final_txid = json_strdup(mfc, buf, txid_tok);
|
|
}
|
|
|
|
/* We done !? */
|
|
dest->psbt = tal_free(dest->psbt);
|
|
return after_openchannel_signed(mfc);
|
|
}
|
|
|
|
static struct command_result *
|
|
openchannel_signed_err(struct command *cmd,
|
|
const char *buf,
|
|
const jsmntok_t *error,
|
|
struct multifundchannel_destination *dest)
|
|
{
|
|
struct multifundchannel_command *mfc = dest->mfc;
|
|
const jsmntok_t *code_tok;
|
|
|
|
plugin_log(mfc->cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64", dest %u: "
|
|
"failed! openchannel_signed %s: %.*s.",
|
|
mfc->id, dest->index,
|
|
node_id_to_hexstr(tmpctx, &dest->id),
|
|
json_tok_full_len(error),
|
|
json_tok_full(buf, error));
|
|
|
|
code_tok = json_get_member(buf, error, "code");
|
|
if (!code_tok)
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_signed` failure did not have `code`? "
|
|
"%.*s",
|
|
json_tok_full_len(error),
|
|
json_tok_full(buf, error));
|
|
if (!json_to_errcode(buf, code_tok, &dest->code))
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_signed` has unparseable `code`? "
|
|
"%.*s",
|
|
json_tok_full_len(code_tok),
|
|
json_tok_full(buf, code_tok));
|
|
|
|
fail_destination(dest, take(json_strdup(NULL, buf, error)));
|
|
return after_openchannel_signed(mfc);
|
|
}
|
|
|
|
static void
|
|
openchannel_signed_dest(struct multifundchannel_destination *dest)
|
|
{
|
|
struct multifundchannel_command *mfc = dest->mfc;
|
|
struct command *cmd = mfc->cmd;
|
|
struct out_req *req;
|
|
|
|
plugin_log(cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64", dest %u: `openchannel_signed` %s "
|
|
"psbt %s",
|
|
mfc->id, dest->index,
|
|
type_to_string(tmpctx, struct channel_id, &dest->channel_id),
|
|
type_to_string(tmpctx, struct wally_psbt, dest->psbt));
|
|
|
|
req = jsonrpc_request_start(cmd->plugin,
|
|
cmd,
|
|
"openchannel_signed",
|
|
&openchannel_signed_ok,
|
|
&openchannel_signed_err,
|
|
dest);
|
|
json_add_channel_id(req->js, "channel_id", &dest->channel_id);
|
|
json_add_psbt(req->js, "signed_psbt", dest->psbt);
|
|
|
|
send_outreq(cmd->plugin, req);
|
|
}
|
|
|
|
static struct command_result *
|
|
perform_openchannel_signed(struct multifundchannel_command *mfc)
|
|
{
|
|
plugin_log(mfc->cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64": parallel `openchannel_signed`.",
|
|
mfc->id);
|
|
|
|
/* Check that every dest is in the right state */
|
|
for (size_t i = 0; i < tal_count(mfc->destinations); i++) {
|
|
struct multifundchannel_destination *dest;
|
|
dest = &mfc->destinations[i];
|
|
if (dest->state != MULTIFUNDCHANNEL_SIGNED) {
|
|
// FIXME: these channels are all borked.
|
|
redo_multiopenchannel(mfc, "openchannel_signed");
|
|
}
|
|
}
|
|
|
|
if (!psbt_finalize(mfc->psbt))
|
|
plugin_err(mfc->cmd->plugin,
|
|
"mfc %"PRIu64": Unable to finalize parent PSBT "
|
|
"%s",
|
|
type_to_string(tmpctx, struct wally_psbt,
|
|
mfc->psbt));
|
|
|
|
mfc->pending = tal_count(mfc->destinations);
|
|
for (size_t i = 0; i < tal_count(mfc->destinations); i++) {
|
|
/* We need to 'port' all of the sigs down to the
|
|
* destination PSBTs */
|
|
update_node_psbt(mfc, mfc->psbt,
|
|
&mfc->destinations[i].psbt);
|
|
openchannel_signed_dest(&mfc->destinations[i]);
|
|
}
|
|
|
|
assert(mfc->pending != 0);
|
|
return command_still_pending(mfc->cmd);
|
|
}
|
|
|
|
static struct command_result *
|
|
after_psbt_signed(struct command *cmd,
|
|
const char *buf,
|
|
const jsmntok_t *result,
|
|
struct multifundchannel_command *mfc)
|
|
{
|
|
const jsmntok_t *field;
|
|
struct wally_psbt *signed_psbt;
|
|
|
|
plugin_log(mfc->cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64": `signpsbt` completed",
|
|
mfc->id);
|
|
|
|
field = json_get_member(buf, result, "signed_psbt");
|
|
if (!field)
|
|
plugin_err(mfc->cmd->plugin,
|
|
"`signpsbt` did not return 'signed_psbt'? %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
if (!json_to_psbt(tmpctx, buf, field, &signed_psbt))
|
|
plugin_err(mfc->cmd->plugin,
|
|
"`signpsbt` returned invalid 'signed_psbt' %.*s",
|
|
json_tok_full_len(field),
|
|
json_tok_full(buf, field));
|
|
|
|
tal_free(mfc->psbt);
|
|
mfc->psbt = tal_steal(mfc, signed_psbt);
|
|
|
|
return perform_openchannel_signed(mfc);
|
|
}
|
|
|
|
static struct command_result *
|
|
collect_sigs(struct multifundchannel_command *mfc)
|
|
{
|
|
/* We need to sign the PSBT, we also need to
|
|
* wait for all of the sigs to come in */
|
|
struct out_req *req;
|
|
struct bitcoin_txid mfc_txid;
|
|
psbt_txid(NULL, mfc->psbt, &mfc_txid, NULL);
|
|
|
|
|
|
/* But first! we sanity check that everyone's
|
|
* expecting the same funding txid */
|
|
for (size_t i = 0; i < tal_count(mfc->destinations); i++) {
|
|
struct multifundchannel_destination *dest;
|
|
struct bitcoin_txid dest_txid;
|
|
dest = &mfc->destinations[i];
|
|
|
|
assert(dest->state == MULTIFUNDCHANNEL_SECURED ||
|
|
dest->state == MULTIFUNDCHANNEL_SIGNED);
|
|
psbt_txid(NULL, dest->psbt, &dest_txid, NULL);
|
|
|
|
assert(bitcoin_txid_eq(&mfc_txid, &dest_txid));
|
|
}
|
|
|
|
/* Now we sign our inputs. You do remember which inputs
|
|
* are yours, right? */
|
|
plugin_log(mfc->cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64": signpsbt.", mfc->id);
|
|
|
|
req = jsonrpc_request_start(mfc->cmd->plugin, mfc->cmd,
|
|
"signpsbt",
|
|
&after_psbt_signed,
|
|
&mfc_forward_error,
|
|
mfc);
|
|
json_add_psbt(req->js, "psbt", mfc->psbt);
|
|
return send_outreq(mfc->cmd->plugin, req);
|
|
}
|
|
|
|
static void
|
|
openchannel_dest_signed(struct multifundchannel_command *mfc)
|
|
{
|
|
bool ready = true;
|
|
for (size_t i = 0; i < tal_count(mfc->destinations); i++)
|
|
ready &= mfc->destinations[i].state ==
|
|
MULTIFUNDCHANNEL_SIGNED;
|
|
|
|
if (ready)
|
|
collect_sigs(mfc);
|
|
}
|
|
|
|
static void json_peer_sigs(struct command *cmd,
|
|
const char *buf,
|
|
const jsmntok_t *params)
|
|
{
|
|
const jsmntok_t *cid_tok, *psbt_tok;
|
|
struct channel_id cid;
|
|
const struct wally_psbt *psbt;
|
|
struct multifundchannel_destination *dest;
|
|
|
|
cid_tok = json_delve(buf, params, ".openchannel_peer_sigs.channel_id");
|
|
if (!cid_tok)
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_peer_sigs` notification did not "
|
|
"send 'channel_id'? %.*s",
|
|
json_tok_full_len(params),
|
|
json_tok_full(buf, params));
|
|
if (!json_to_channel_id(buf, cid_tok, &cid))
|
|
plugin_err(cmd->plugin,
|
|
"Unable to parse openchannel_peer_sigs.channel_id "
|
|
"%.*s",
|
|
json_tok_full_len(params),
|
|
json_tok_full(buf, params));
|
|
|
|
psbt_tok= json_delve(buf, params, ".openchannel_peer_sigs.signed_psbt");
|
|
if (!psbt_tok)
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_peer_sigs` notification did not "
|
|
"include 'signed_psbt'? %.*s",
|
|
json_tok_full_len(params),
|
|
json_tok_full(buf, params));
|
|
|
|
if (!json_to_psbt(cmd, buf, psbt_tok,
|
|
cast_const2(struct wally_psbt **, &psbt)))
|
|
plugin_err(cmd->plugin,
|
|
"Unable to parse openchannel_peer_sigs.signed_psbt "
|
|
"%.*s",
|
|
json_tok_full_len(params),
|
|
json_tok_full(buf, params));
|
|
|
|
/* Find the destination that's got this channel_id on it! */
|
|
dest = find_dest_by_channel_id(&cid);
|
|
if (!dest) {
|
|
/* if there's no pending destination... whatever */
|
|
plugin_log(cmd->plugin, LOG_DBG,
|
|
"mfc ??: `openchannel_peer_sigs` no "
|
|
"pending dest found for channel_id %s",
|
|
type_to_string(tmpctx, struct channel_id, &cid));
|
|
return;
|
|
}
|
|
|
|
plugin_log(cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64":`openchannel_peer_sigs` notice received for"
|
|
" channel %s",
|
|
dest->mfc->id,
|
|
tal_hexstr(tmpctx, &cid, sizeof(cid)));
|
|
|
|
assert(dest->state == MULTIFUNDCHANNEL_SECURED);
|
|
|
|
/* Combine with the parent. Unknown map dupes are ignored,
|
|
* so the updated serial_id should persist on the parent */
|
|
tal_wally_start();
|
|
if (wally_psbt_combine(dest->mfc->psbt, psbt) != WALLY_OK)
|
|
plugin_err(cmd->plugin,
|
|
"mfc %"PRIu64": Unable to combine signed "
|
|
"PSBT with roll-up. "
|
|
"Signed %s, prev %s", dest->mfc->id,
|
|
type_to_string(tmpctx, struct wally_psbt, psbt),
|
|
type_to_string(tmpctx, struct wally_psbt,
|
|
dest->mfc->psbt));
|
|
|
|
tal_wally_end(dest->mfc->psbt);
|
|
dest->state = MULTIFUNDCHANNEL_SIGNED;
|
|
openchannel_dest_signed(dest->mfc);
|
|
}
|
|
|
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*
|
|
* The v2 of channel establishment uses a different RPC flow:
|
|
* `openchannel_init`, `openchannel_update`, && `openchannel_signed`
|
|
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*
|
|
*/
|
|
|
|
/* We call it circularly, til finished */
|
|
static struct command_result *
|
|
perform_openchannel_update(struct multifundchannel_command *mfc);
|
|
|
|
static struct command_result *
|
|
openchannel_update_returned(struct multifundchannel_destination *dest)
|
|
{
|
|
struct multifundchannel_command *mfc = dest->mfc;
|
|
|
|
--mfc->pending;
|
|
if (mfc->pending == 0)
|
|
return perform_openchannel_update(mfc);
|
|
else
|
|
return command_still_pending(mfc->cmd);
|
|
}
|
|
|
|
static struct command_result *
|
|
openchannel_update_ok(struct command *cmd,
|
|
const char *buf,
|
|
const jsmntok_t *result,
|
|
struct multifundchannel_destination *dest)
|
|
{
|
|
struct multifundchannel_command *mfc = dest->mfc;
|
|
const jsmntok_t *psbt_tok, *done_tok;
|
|
bool done;
|
|
|
|
plugin_log(mfc->cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64", dest %u: openchannel_update %s returned.",
|
|
mfc->id, dest->index,
|
|
node_id_to_hexstr(tmpctx, &dest->id));
|
|
|
|
assert(!dest->updated_psbt);
|
|
psbt_tok = json_get_member(buf, result, "psbt");
|
|
if (!psbt_tok)
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_update` did not return "
|
|
"'psbt': %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
|
|
if (!json_to_psbt(dest->mfc, buf, psbt_tok, &dest->updated_psbt))
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_update` returned invalid "
|
|
"'psbt': %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
|
|
/* Should we check that the channel id is correct? */
|
|
|
|
done_tok = json_get_member(buf, result, "commitments_secured");
|
|
if (!done_tok)
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_update` failed to return "
|
|
"'commitments_secured': %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
|
|
if (!json_to_bool(buf, done_tok, &done))
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_update` returned invalid "
|
|
"'commitments_secured': %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
|
|
if (done) {
|
|
const jsmntok_t *outnum_tok;
|
|
|
|
outnum_tok = json_get_member(buf, result, "funding_outnum");
|
|
if (!outnum_tok)
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_update` did not return "
|
|
"'funding_outnum': %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
|
|
if (!json_to_number(buf, outnum_tok, &dest->outnum))
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_update` returned invalid "
|
|
"'funding_outnum': %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
|
|
/* It's possible they beat us to the SIGNED flag,
|
|
* in which case we just let that be the more senior
|
|
* state position */
|
|
if (dest->state != MULTIFUNDCHANNEL_SIGNED)
|
|
dest->state = MULTIFUNDCHANNEL_SECURED;
|
|
} else
|
|
dest->state = MULTIFUNDCHANNEL_UPDATED;
|
|
|
|
return openchannel_update_returned(dest);
|
|
}
|
|
|
|
static struct command_result *
|
|
openchannel_update_err(struct command *cmd,
|
|
const char *buf,
|
|
const jsmntok_t *error,
|
|
struct multifundchannel_destination *dest)
|
|
{
|
|
struct multifundchannel_command *mfc = dest->mfc;
|
|
const jsmntok_t *code_tok;
|
|
|
|
plugin_err(mfc->cmd->plugin,
|
|
"mfc %"PRIu64", dest %u: "
|
|
"failed! `openchannel_update` %s: %.*s",
|
|
mfc->id, dest->index,
|
|
node_id_to_hexstr(tmpctx, &dest->id),
|
|
json_tok_full_len(error), json_tok_full(buf, error));
|
|
|
|
code_tok = json_get_member(buf, error, "code");
|
|
if (!code_tok)
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_update` failure missing "
|
|
"`code`? %.*s",
|
|
json_tok_full_len(error),
|
|
json_tok_full(buf, error));
|
|
if (!json_to_errcode(buf, code_tok, &dest->code))
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_update` returned unparseable `code`? "
|
|
"%.*s",
|
|
json_tok_full_len(error),
|
|
json_tok_full(buf, error));
|
|
|
|
fail_destination(dest, take(json_strdup(NULL, buf, error)));
|
|
return openchannel_update_returned(dest);
|
|
}
|
|
|
|
static void
|
|
openchannel_update_dest(struct multifundchannel_destination *dest)
|
|
{
|
|
struct multifundchannel_command *mfc = dest->mfc;
|
|
struct command *cmd = mfc->cmd;
|
|
struct out_req *req;
|
|
|
|
plugin_log(cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64", dest %u: `openchannel_update` %s "
|
|
"with psbt %s",
|
|
mfc->id, dest->index,
|
|
node_id_to_hexstr(tmpctx, &dest->id),
|
|
type_to_string(tmpctx, struct wally_psbt, dest->psbt));
|
|
|
|
req = jsonrpc_request_start(cmd->plugin,
|
|
cmd,
|
|
"openchannel_update",
|
|
&openchannel_update_ok,
|
|
&openchannel_update_err,
|
|
dest);
|
|
json_add_channel_id(req->js, "channel_id", &dest->channel_id);
|
|
json_add_psbt(req->js, "psbt", dest->psbt);
|
|
|
|
send_outreq(cmd->plugin, req);
|
|
}
|
|
|
|
static struct command_result *
|
|
perform_openchannel_update(struct multifundchannel_command *mfc)
|
|
{
|
|
size_t i, ready_count = 0;
|
|
|
|
plugin_log(mfc->cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64": parallel `openchannel_update`.",
|
|
mfc->id);
|
|
|
|
/* First we check for failures/finished state */
|
|
for (i = 0; i < tal_count(mfc->destinations); i++) {
|
|
struct multifundchannel_destination *dest;
|
|
dest = &mfc->destinations[i];
|
|
|
|
if (dest->state == MULTIFUNDCHANNEL_FAILED)
|
|
return redo_multiopenchannel(mfc,
|
|
"openchannel_update");
|
|
|
|
/* If any *one* is secured or signed, they should all
|
|
* be done. */
|
|
if (dest->state == MULTIFUNDCHANNEL_SECURED ||
|
|
dest->state == MULTIFUNDCHANNEL_SIGNED) {
|
|
ready_count++;
|
|
continue;
|
|
}
|
|
|
|
assert(dest->state == MULTIFUNDCHANNEL_UPDATED ||
|
|
dest->state == MULTIFUNDCHANNEL_STARTED);
|
|
}
|
|
|
|
// FIXME: how many is the total count here?
|
|
if (ready_count == tal_count(mfc->destinations)) {
|
|
return command_still_pending(mfc->cmd);
|
|
}
|
|
|
|
/* Then, we update the parent with every node's result */
|
|
for (i = 0; i < tal_count(mfc->destinations); i++) {
|
|
struct multifundchannel_destination *dest;
|
|
dest = &mfc->destinations[i];
|
|
|
|
if (!update_parent_psbt(mfc, dest, dest->psbt,
|
|
dest->updated_psbt,
|
|
&mfc->psbt))
|
|
return redo_multiopenchannel(mfc,
|
|
"openchannel_init_parent");
|
|
/* Get everything sorted correctly */
|
|
psbt_sort_by_serial_id(mfc->psbt);
|
|
|
|
tal_free(dest->psbt);
|
|
dest->psbt = dest->updated_psbt;
|
|
dest->updated_psbt = NULL;
|
|
}
|
|
|
|
/* Next we update the view of every destination with the
|
|
* parent viewset */
|
|
for (i = 0; i < tal_count(mfc->destinations); i++) {
|
|
struct multifundchannel_destination *dest;
|
|
dest = &mfc->destinations[i];
|
|
|
|
if (!update_node_psbt(mfc, mfc->psbt, &dest->psbt))
|
|
return redo_multiopenchannel(mfc,
|
|
"openchannel_init_node");
|
|
}
|
|
|
|
mfc->pending = tal_count(mfc->destinations);
|
|
for (i = 0; i < tal_count(mfc->destinations); i++)
|
|
openchannel_update_dest(&mfc->destinations[i]);
|
|
|
|
assert(mfc->pending != 0);
|
|
return command_still_pending(mfc->cmd);
|
|
}
|
|
|
|
static struct command_result *
|
|
after_openchannel_init(struct multifundchannel_command *mfc)
|
|
{
|
|
unsigned int i;
|
|
|
|
plugin_log(mfc->cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64": parallel openchannel_init done.",
|
|
mfc->id);
|
|
|
|
/* Check if any openchannel_init failed. */
|
|
for (i = 0; i < tal_count(mfc->destinations); ++i) {
|
|
struct multifundchannel_destination *dest;
|
|
|
|
dest = &mfc->destinations[i];
|
|
|
|
assert(dest->state == MULTIFUNDCHANNEL_STARTED
|
|
|| dest->state == MULTIFUNDCHANNEL_FAILED);
|
|
|
|
if (dest->state != MULTIFUNDCHANNEL_FAILED)
|
|
continue;
|
|
|
|
/* One of them failed, oh no. */
|
|
return redo_multiopenchannel(mfc, "openchannel_init");
|
|
}
|
|
|
|
/* We need to add the change output here, for now. Will
|
|
* remove when fundchannel flow is merged */
|
|
if (mfc->change_needed) {
|
|
struct wally_psbt_output *out;
|
|
u16 serial_id;
|
|
|
|
out = psbt_append_output(mfc->psbt,
|
|
mfc->change_scriptpubkey,
|
|
mfc->change_amount);
|
|
|
|
serial_id = psbt_new_output_serial(mfc->psbt, TX_INITIATOR);
|
|
psbt_output_set_serial_id(mfc->psbt, out, serial_id);
|
|
}
|
|
|
|
/* Now we stash the 'mfc' command, so when/if
|
|
* signature notifications start coming in, we'll catch them. */
|
|
register_mfc(mfc);
|
|
|
|
return perform_openchannel_update(mfc);
|
|
}
|
|
|
|
static struct command_result *
|
|
openchannel_init_done(struct multifundchannel_destination *dest)
|
|
{
|
|
struct multifundchannel_command *mfc = dest->mfc;
|
|
|
|
--mfc->pending;
|
|
if (mfc->pending == 0)
|
|
return after_openchannel_init(mfc);
|
|
else
|
|
return command_still_pending(mfc->cmd);
|
|
}
|
|
|
|
static struct command_result *
|
|
openchannel_init_ok(struct command *cmd,
|
|
const char *buf,
|
|
const jsmntok_t *result,
|
|
struct multifundchannel_destination *dest)
|
|
{
|
|
struct multifundchannel_command *mfc = dest->mfc;
|
|
const jsmntok_t *psbt_tok;
|
|
const jsmntok_t *channel_id_tok;
|
|
const jsmntok_t *funding_serial_tok;
|
|
|
|
plugin_log(mfc->cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64", dest %u: openchannel_init %s done.",
|
|
mfc->id, dest->index,
|
|
node_id_to_hexstr(tmpctx, &dest->id));
|
|
|
|
/* We've got the PSBT and channel_id here */
|
|
psbt_tok = json_get_member(buf, result, "psbt");
|
|
if (!psbt_tok)
|
|
plugin_err(cmd->plugin,
|
|
"openchannel_init did not return "
|
|
"'psbt': %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
if (!json_to_psbt(dest->mfc, buf, psbt_tok, &dest->updated_psbt))
|
|
plugin_err(cmd->plugin,
|
|
"openchannel_init returned invalid "
|
|
"'psbt': %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
|
|
channel_id_tok = json_get_member(buf, result, "channel_id");
|
|
if (!channel_id_tok)
|
|
plugin_err(cmd->plugin,
|
|
"openchannel_init did not return "
|
|
"'channel_id': %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
json_to_channel_id(buf, channel_id_tok, &dest->channel_id);
|
|
|
|
funding_serial_tok = json_get_member(buf, result,
|
|
"funding_serial");
|
|
if (!funding_serial_tok)
|
|
plugin_err(cmd->plugin,
|
|
"openchannel_init did not return "
|
|
"'funding_serial': %.*s",
|
|
json_tok_full_len(result),
|
|
json_tok_full(buf, result));
|
|
json_to_u64(buf, funding_serial_tok, &dest->funding_serial);
|
|
|
|
dest->state = MULTIFUNDCHANNEL_STARTED;
|
|
|
|
/* Port any updates onto 'parent' PSBT */
|
|
if (!update_parent_psbt(dest->mfc, dest, dest->psbt,
|
|
dest->updated_psbt, &mfc->psbt)) {
|
|
fail_destination(dest,
|
|
take(tal_fmt(NULL, "Unable to update parent"
|
|
" with node's PSBT")));
|
|
}
|
|
|
|
/* Clone updated-psbt to psbt, so original changeset
|
|
* will be empty, but tallocate it so we can leave tal_free
|
|
* logic in `perform_openchannel_update` the same. */
|
|
tal_wally_start();
|
|
wally_psbt_clone_alloc(dest->updated_psbt, 0, &dest->psbt);
|
|
tal_wally_end(tal_steal(mfc, dest->updated_psbt));
|
|
return openchannel_init_done(dest);
|
|
}
|
|
|
|
static struct command_result *
|
|
openchannel_init_err(struct command *cmd,
|
|
const char *buf,
|
|
const jsmntok_t *error,
|
|
struct multifundchannel_destination *dest)
|
|
{
|
|
struct multifundchannel_command *mfc = dest->mfc;
|
|
const jsmntok_t *code_tok;
|
|
|
|
plugin_err(mfc->cmd->plugin,
|
|
"mfc %"PRIu64", dest %u: "
|
|
"failed! openchannel_init %s: %.*s.",
|
|
mfc->id, dest->index,
|
|
node_id_to_hexstr(tmpctx, &dest->id),
|
|
json_tok_full_len(error),
|
|
json_tok_full(buf, error));
|
|
|
|
code_tok = json_get_member(buf, error, "code");
|
|
if (!code_tok)
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_init` failure did not have `code`? "
|
|
"%.*s",
|
|
json_tok_full_len(error),
|
|
json_tok_full(buf, error));
|
|
if (!json_to_errcode(buf, code_tok, &dest->code))
|
|
plugin_err(cmd->plugin,
|
|
"`openchannel_init` has unparseable `code`? "
|
|
"%.*s",
|
|
json_tok_full_len(code_tok),
|
|
json_tok_full(buf, code_tok));
|
|
|
|
fail_destination(dest, take(json_strdup(NULL, buf, error)));
|
|
return openchannel_init_done(dest);
|
|
}
|
|
|
|
struct command_result *
|
|
openchannel_init_dest(struct multifundchannel_destination *dest)
|
|
{
|
|
struct multifundchannel_command *mfc = dest->mfc;
|
|
struct command *cmd = mfc->cmd;
|
|
struct out_req *req;
|
|
|
|
plugin_log(cmd->plugin, LOG_DBG,
|
|
"mfc %"PRIu64", dest %u: openchannel_init %s.",
|
|
mfc->id, dest->index,
|
|
node_id_to_hexstr(tmpctx, &dest->id));
|
|
|
|
req = jsonrpc_request_start(cmd->plugin, cmd,
|
|
"openchannel_init",
|
|
&openchannel_init_ok,
|
|
&openchannel_init_err,
|
|
dest);
|
|
|
|
json_add_node_id(req->js, "id", &dest->id);
|
|
assert(!dest->all);
|
|
json_add_string(req->js, "amount",
|
|
fmt_amount_sat(tmpctx, &dest->amount));
|
|
|
|
/* Copy the original parent down */
|
|
tal_wally_start();
|
|
wally_psbt_clone_alloc(mfc->psbt, 0, &dest->psbt);
|
|
tal_wally_end(tal_steal(mfc, dest->psbt));
|
|
|
|
json_add_psbt(req->js, "initialpsbt", dest->psbt);
|
|
if (mfc->cmtmt_feerate_str)
|
|
json_add_string(req->js, "commitment_feerate",
|
|
mfc->cmtmt_feerate_str);
|
|
if (mfc->feerate_str) {
|
|
json_add_string(req->js, "funding_feerate",
|
|
mfc->feerate_str);
|
|
|
|
/* If there's no commitment feerate provided, we assume
|
|
* that the same feerate is to be used on both. This mimics
|
|
* the behavior of the old-style feerate stuffs */
|
|
if (!mfc->cmtmt_feerate_str)
|
|
json_add_string(req->js, "commitment_feerate",
|
|
mfc->feerate_str);
|
|
}
|
|
json_add_bool(req->js, "announce", dest->announce);
|
|
if (dest->close_to_str)
|
|
json_add_string(req->js, "close_to", dest->close_to_str);
|
|
|
|
if (amount_msat_greater(dest->push_msat, AMOUNT_MSAT(0)))
|
|
plugin_log(cmd->plugin, LOG_INFORM,
|
|
"Using openchannel for %s open, "
|
|
"ignoring `push_msat` of %s",
|
|
node_id_to_hexstr(tmpctx, &dest->id),
|
|
type_to_string(tmpctx, struct amount_msat,
|
|
&dest->push_msat));
|
|
|
|
return send_outreq(cmd->plugin, req);
|
|
}
|
|
|
|
void openchannel_init(struct plugin *p, const char *b, const jsmntok_t *t)
|
|
{
|
|
/* Initialize our list! */
|
|
list_head_init(&mfc_commands);
|
|
}
|
|
|
|
const struct plugin_notification openchannel_notifs[] = {
|
|
{
|
|
"openchannel_peer_sigs",
|
|
json_peer_sigs,
|
|
}
|
|
};
|
|
const size_t num_openchannel_notifs = ARRAY_SIZE(openchannel_notifs);
|