diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index 91e8b95a1..1e7f5414f 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -51,6 +51,12 @@ size_t dest_count(const struct multifundchannel_command *mfc, return count; } +/* Return true if this destination failed, false otherwise. */ +static bool dest_failed(struct multifundchannel_destination *dest) +{ + return dest->state == MULTIFUNDCHANNEL_FAILED; +} + /*----------------------------------------------------------------------------- Command Cleanup -----------------------------------------------------------------------------*/ @@ -581,304 +587,6 @@ connect_dest(struct multifundchannel_destination *dest) send_outreq(cmd->plugin, req); } -/*---------------------------------------------------------------------------*/ - -/*~ Create an initial funding PSBT. - -This creation of the initial funding PSBT is solely to reserve inputs for -our use. -This lets us initiate later with fundchannel_start with confidence that we -can actually afford the channels we will create. -*/ - -static struct command_result * -perform_channel_start(struct multifundchannel_command *mfc); - -static struct command_result * -mfc_psbt_acquired(struct multifundchannel_command *mfc) -{ - /* Add serials to all of our input/outputs, so they're stable - * for the life of the tx */ - psbt_add_serials(mfc->psbt, TX_INITIATOR); - - /* We also mark all of our inputs as *ours*, so we - * can easily identify them for `signpsbt`, later */ - for (size_t i = 0; i < mfc->psbt->num_inputs; i++) - psbt_input_mark_ours(mfc->psbt, &mfc->psbt->inputs[i]); - - return perform_channel_start(mfc); -} - -static struct command_result * -after_newaddr(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct multifundchannel_command *mfc) -{ - const jsmntok_t *field; - - field = json_get_member(buf, result, "bech32"); - if (!field) - plugin_err(cmd->plugin, - "No bech32 field in newaddr result: %.*s", - json_tok_full_len(result), - json_tok_full(buf, result)); - if (json_to_address_scriptpubkey(mfc, chainparams, buf, field, - &mfc->change_scriptpubkey) - != ADDRESS_PARSE_SUCCESS) - plugin_err(cmd->plugin, - "Unparseable bech32 field in newaddr result: %.*s", - json_tok_full_len(result), - json_tok_full(buf, result)); - - return mfc_psbt_acquired(mfc); -} - -static struct command_result * -acquire_change_address(struct multifundchannel_command *mfc) -{ - struct out_req *req; - req = jsonrpc_request_start(mfc->cmd->plugin, mfc->cmd, - "newaddr", - &after_newaddr, &mfc_forward_error, - mfc); - json_add_string(req->js, "addresstype", "bech32"); - return send_outreq(mfc->cmd->plugin, req); -} - -static struct command_result * -handle_mfc_change(struct multifundchannel_command *mfc) -{ - size_t change_weight; - struct amount_sat change_fee, fee_paid, total_fee; - struct amount_sat change_min_limit; - - /* Determine if adding a change output is worth it. - * Get the weight of a change output and how much it - * costs. - */ - change_weight = bitcoin_tx_output_weight( - BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN); - - /* To avoid 'off-by-one' errors due to rounding down - * (which we do in `amount_tx_fee`), we find the total calculated - * fees (estimated_weight + change weight @ feerate) and subtract - * the originally calculated fees (estimated_weight @ feerate) */ - fee_paid = amount_tx_fee(mfc->feerate_per_kw, - mfc->estimated_final_weight); - total_fee = amount_tx_fee(mfc->feerate_per_kw, - mfc->estimated_final_weight + change_weight); - if (!amount_sat_sub(&change_fee, total_fee, fee_paid)) - abort(); - - /* The limit is equal to the change_fee plus the dust limit. */ - if (!amount_sat_add(&change_min_limit, - change_fee, chainparams->dust_limit)) - plugin_err(mfc->cmd->plugin, - "Overflow dust limit and change fee."); - - /* Is the excess over the limit? */ - if (amount_sat_greater(mfc->excess_sat, change_min_limit)) { - bool ok = amount_sat_sub(&mfc->change_amount, - mfc->excess_sat, change_fee); - assert(ok); - mfc->change_needed = true; - if (!mfc->change_scriptpubkey) - return acquire_change_address(mfc); - } else - mfc->change_needed = false; - - return mfc_psbt_acquired(mfc); -} - -/* If one of the destinations specified "all", figure out how much that is. */ -static struct command_result * -compute_mfc_all(struct multifundchannel_command *mfc) -{ - size_t all_index = SIZE_MAX; - struct multifundchannel_destination *all_dest; - - assert(has_all(mfc)); - - for (size_t i = 0; i < tal_count(mfc->destinations); ++i) { - struct multifundchannel_destination *dest; - dest = &mfc->destinations[i]; - if (dest->all) { - assert(all_index == SIZE_MAX); - all_index = i; - continue; - } - /* Subtract the amount from the excess. */ - if (!amount_sat_sub(&mfc->excess_sat, - mfc->excess_sat, dest->amount)) - /* Not enough funds! */ - return mfc_fail(mfc, FUND_CANNOT_AFFORD, - "Insufficient funds."); - } - assert(all_index != SIZE_MAX); - all_dest = &mfc->destinations[all_index]; - - /* Is the excess above the dust amount? */ - if (amount_sat_less(mfc->excess_sat, chainparams->dust_limit)) - return mfc_fail(mfc, FUND_OUTPUT_IS_DUST, - "Output 'all' %s would be dust", - type_to_string(tmpctx, struct amount_sat, - &mfc->excess_sat)); - - /* Assign the remainder to the 'all' output. */ - all_dest->amount = mfc->excess_sat; - if (!feature_negotiated(plugin_feature_set(mfc->cmd->plugin), - all_dest->their_features, - OPT_LARGE_CHANNELS) - && amount_sat_greater(all_dest->amount, - chainparams->max_funding)) - all_dest->amount = chainparams->max_funding; - /* Remove it from the excess. */ - bool ok = amount_sat_sub(&mfc->excess_sat, - mfc->excess_sat, all_dest->amount); - assert(ok); - /* Remove the 'all' flag. */ - all_dest->all = false; - - /* Continue. */ - return handle_mfc_change(mfc); -} - -static struct command_result * -after_fundpsbt(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct multifundchannel_command *mfc) -{ - const jsmntok_t *field; - - plugin_log(mfc->cmd->plugin, LOG_DBG, - "mfc %"PRIu64": %s done.", - mfc->id, mfc->utxos_str ? "utxopsbt" : "fundpsbt"); - - field = json_get_member(buf, result, "psbt"); - if (!field) - goto fail; - - mfc->psbt = psbt_from_b64(mfc, - buf + field->start, - field->end - field->start); - if (!mfc->psbt) - goto fail; - - field = json_get_member(buf, result, "feerate_per_kw"); - if (!field || !json_to_u32(buf, field, &mfc->feerate_per_kw)) - goto fail; - - field = json_get_member(buf, result, "estimated_final_weight"); - if (!field || !json_to_u32(buf, field, &mfc->estimated_final_weight)) - goto fail; - - /* msat LOL. */ - field = json_get_member(buf, result, "excess_msat"); - if (!field || !parse_amount_sat(&mfc->excess_sat, - buf + field->start, - field->end - field->start)) - goto fail; - - if (has_all(mfc)) - return compute_mfc_all(mfc); - return handle_mfc_change(mfc); - -fail: - plugin_err(mfc->cmd->plugin, - "Unexpected result from fundpsbt/utxopsbt: %.*s", - json_tok_full_len(result), - json_tok_full(buf, result)); - -} - - -static struct command_result * -perform_fundpsbt(struct multifundchannel_command *mfc) -{ - struct out_req *req; - - /* If the user specified utxos we should use utxopsbt instead - * of fundpsbt. */ - if (mfc->utxos_str) { - plugin_log(mfc->cmd->plugin, LOG_DBG, - "mfc %"PRIu64": utxopsbt.", - mfc->id); - - req = jsonrpc_request_start(mfc->cmd->plugin, - mfc->cmd, - "utxopsbt", - &after_fundpsbt, - &mfc_forward_error, - mfc); - json_add_jsonstr(req->js, "utxos", mfc->utxos_str); - json_add_bool(req->js, "reservedok", false); - } else { - plugin_log(mfc->cmd->plugin, LOG_DBG, - "mfc %"PRIu64": fundpsbt.", - mfc->id); - - req = jsonrpc_request_start(mfc->cmd->plugin, - mfc->cmd, - "fundpsbt", - &after_fundpsbt, - &mfc_forward_error, - mfc); - json_add_u32(req->js, "minconf", mfc->minconf); - } - - /* The entire point is to reserve the inputs. */ - json_add_bool(req->js, "reserve", true); - /* How much do we need to reserve? */ - if (has_all(mfc)) - json_add_string(req->js, "satoshi", "all"); - else { - struct amount_sat sum = AMOUNT_SAT(0); - for (size_t i = 0; i < tal_count(mfc->destinations); ++i) { - if (!amount_sat_add(&sum, - sum, mfc->destinations[i].amount)) - return mfc_fail(mfc, JSONRPC2_INVALID_PARAMS, - "Overflow while summing " - "destination values."); - } - json_add_string(req->js, "satoshi", - type_to_string(tmpctx, struct amount_sat, - &sum)); - } - json_add_string(req->js, "feerate", - mfc->feerate_str ? mfc->feerate_str : "normal"); - - { - size_t startweight; - size_t num_outs = tal_count(mfc->destinations); - /* Assume 1 input. - * As long as lightningd does not select more than 252 - * inputs, that estimation should be correct. - */ - startweight = bitcoin_tx_core_weight(1, num_outs) - + ( bitcoin_tx_output_weight( - BITCOIN_SCRIPTPUBKEY_P2WSH_LEN) - * num_outs - ); - json_add_string(req->js, "startweight", - tal_fmt(tmpctx, "%zu", startweight)); - } - - /* If we've got v2 opens, we need to use a min weight of 110. */ - /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #3: - * The minimum witness weight for an input is 110. - */ - if (dest_count(mfc, OPEN_CHANNEL) > 0) { - json_add_string(req->js, "min_witness_weight", - tal_fmt(tmpctx, "%u", 110)); - } - - - return send_outreq(mfc->cmd->plugin, req); -} - - /*---------------------------------------------------------------------------*/ /*~ @@ -1627,12 +1335,6 @@ fundchannel_complete_dest(struct multifundchannel_destination *dest) send_outreq(cmd->plugin, req); } -/* Return true if this destination failed, false otherwise. */ -static bool dest_failed(struct multifundchannel_destination *dest) -{ - return dest->state == MULTIFUNDCHANNEL_FAILED; -} - void fail_destination(struct multifundchannel_destination *dest, char *error TAKES) { @@ -1644,21 +1346,302 @@ void fail_destination(struct multifundchannel_destination *dest, dest->error = tal_strdup(dest->mfc, error); } -static struct command_result *param_positive_number(struct command *cmd, - const char *name, - const char *buffer, - const jsmntok_t *tok, - unsigned int **num) +/*---------------------------------------------------------------------------*/ + +/*~ Create an initial funding PSBT. + +This creation of the initial funding PSBT is solely to reserve inputs for +our use. +This lets us initiate later with fundchannel_start with confidence that we +can actually afford the channels we will create. +*/ + +static struct command_result * +mfc_psbt_acquired(struct multifundchannel_command *mfc) { - struct command_result *res = param_number(cmd, name, buffer, tok, num); - if (res) - return res; - if (**num == 0) - return command_fail_badparam(cmd, name, buffer, tok, - "should be a positive integer"); - return NULL; + /* Add serials to all of our input/outputs, so they're stable + * for the life of the tx */ + psbt_add_serials(mfc->psbt, TX_INITIATOR); + + /* We also mark all of our inputs as *ours*, so we + * can easily identify them for `signpsbt`, later */ + for (size_t i = 0; i < mfc->psbt->num_inputs; i++) + psbt_input_mark_ours(mfc->psbt, &mfc->psbt->inputs[i]); + + return perform_channel_start(mfc); } +static struct command_result * +after_newaddr(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct multifundchannel_command *mfc) +{ + const jsmntok_t *field; + + field = json_get_member(buf, result, "bech32"); + if (!field) + plugin_err(cmd->plugin, + "No bech32 field in newaddr result: %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + if (json_to_address_scriptpubkey(mfc, chainparams, buf, field, + &mfc->change_scriptpubkey) + != ADDRESS_PARSE_SUCCESS) + plugin_err(cmd->plugin, + "Unparseable bech32 field in newaddr result: %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + + return mfc_psbt_acquired(mfc); +} + +static struct command_result * +acquire_change_address(struct multifundchannel_command *mfc) +{ + struct out_req *req; + req = jsonrpc_request_start(mfc->cmd->plugin, mfc->cmd, + "newaddr", + &after_newaddr, &mfc_forward_error, + mfc); + json_add_string(req->js, "addresstype", "bech32"); + return send_outreq(mfc->cmd->plugin, req); +} + +static struct command_result * +handle_mfc_change(struct multifundchannel_command *mfc) +{ + size_t change_weight; + struct amount_sat change_fee, fee_paid, total_fee; + struct amount_sat change_min_limit; + + /* Determine if adding a change output is worth it. + * Get the weight of a change output and how much it + * costs. + */ + change_weight = bitcoin_tx_output_weight( + BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN); + + /* To avoid 'off-by-one' errors due to rounding down + * (which we do in `amount_tx_fee`), we find the total calculated + * fees (estimated_weight + change weight @ feerate) and subtract + * the originally calculated fees (estimated_weight @ feerate) */ + fee_paid = amount_tx_fee(mfc->feerate_per_kw, + mfc->estimated_final_weight); + total_fee = amount_tx_fee(mfc->feerate_per_kw, + mfc->estimated_final_weight + change_weight); + if (!amount_sat_sub(&change_fee, total_fee, fee_paid)) + abort(); + + /* The limit is equal to the change_fee plus the dust limit. */ + if (!amount_sat_add(&change_min_limit, + change_fee, chainparams->dust_limit)) + plugin_err(mfc->cmd->plugin, + "Overflow dust limit and change fee."); + + /* Is the excess over the limit? */ + if (amount_sat_greater(mfc->excess_sat, change_min_limit)) { + bool ok = amount_sat_sub(&mfc->change_amount, + mfc->excess_sat, change_fee); + assert(ok); + mfc->change_needed = true; + if (!mfc->change_scriptpubkey) + return acquire_change_address(mfc); + } else + mfc->change_needed = false; + + return mfc_psbt_acquired(mfc); +} + +/* If one of the destinations specified "all", figure out how much that is. */ +static struct command_result * +compute_mfc_all(struct multifundchannel_command *mfc) +{ + size_t all_index = SIZE_MAX; + struct multifundchannel_destination *all_dest; + + assert(has_all(mfc)); + + for (size_t i = 0; i < tal_count(mfc->destinations); ++i) { + struct multifundchannel_destination *dest; + dest = &mfc->destinations[i]; + if (dest->all) { + assert(all_index == SIZE_MAX); + all_index = i; + continue; + } + /* Subtract the amount from the excess. */ + if (!amount_sat_sub(&mfc->excess_sat, + mfc->excess_sat, dest->amount)) + /* Not enough funds! */ + return mfc_fail(mfc, FUND_CANNOT_AFFORD, + "Insufficient funds."); + } + assert(all_index != SIZE_MAX); + all_dest = &mfc->destinations[all_index]; + + /* Is the excess above the dust amount? */ + if (amount_sat_less(mfc->excess_sat, chainparams->dust_limit)) + return mfc_fail(mfc, FUND_OUTPUT_IS_DUST, + "Output 'all' %s would be dust", + type_to_string(tmpctx, struct amount_sat, + &mfc->excess_sat)); + + /* Assign the remainder to the 'all' output. */ + all_dest->amount = mfc->excess_sat; + if (!feature_negotiated(plugin_feature_set(mfc->cmd->plugin), + all_dest->their_features, + OPT_LARGE_CHANNELS) + && amount_sat_greater(all_dest->amount, + chainparams->max_funding)) + all_dest->amount = chainparams->max_funding; + /* Remove it from the excess. */ + bool ok = amount_sat_sub(&mfc->excess_sat, + mfc->excess_sat, all_dest->amount); + assert(ok); + /* Remove the 'all' flag. */ + all_dest->all = false; + + /* Continue. */ + return handle_mfc_change(mfc); +} + +static struct command_result * +after_fundpsbt(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct multifundchannel_command *mfc) +{ + const jsmntok_t *field; + + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64": %s done.", + mfc->id, mfc->utxos_str ? "utxopsbt" : "fundpsbt"); + + field = json_get_member(buf, result, "psbt"); + if (!field) + goto fail; + + mfc->psbt = psbt_from_b64(mfc, + buf + field->start, + field->end - field->start); + if (!mfc->psbt) + goto fail; + + field = json_get_member(buf, result, "feerate_per_kw"); + if (!field || !json_to_u32(buf, field, &mfc->feerate_per_kw)) + goto fail; + + field = json_get_member(buf, result, "estimated_final_weight"); + if (!field || !json_to_u32(buf, field, &mfc->estimated_final_weight)) + goto fail; + + /* msat LOL. */ + field = json_get_member(buf, result, "excess_msat"); + if (!field || !parse_amount_sat(&mfc->excess_sat, + buf + field->start, + field->end - field->start)) + goto fail; + + if (has_all(mfc)) + return compute_mfc_all(mfc); + return handle_mfc_change(mfc); + +fail: + plugin_err(mfc->cmd->plugin, + "Unexpected result from fundpsbt/utxopsbt: %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); + +} + + +static struct command_result * +perform_fundpsbt(struct multifundchannel_command *mfc) +{ + struct out_req *req; + + /* If the user specified utxos we should use utxopsbt instead + * of fundpsbt. */ + if (mfc->utxos_str) { + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64": utxopsbt.", + mfc->id); + + req = jsonrpc_request_start(mfc->cmd->plugin, + mfc->cmd, + "utxopsbt", + &after_fundpsbt, + &mfc_forward_error, + mfc); + json_add_jsonstr(req->js, "utxos", mfc->utxos_str); + json_add_bool(req->js, "reservedok", false); + } else { + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64": fundpsbt.", + mfc->id); + + req = jsonrpc_request_start(mfc->cmd->plugin, + mfc->cmd, + "fundpsbt", + &after_fundpsbt, + &mfc_forward_error, + mfc); + json_add_u32(req->js, "minconf", mfc->minconf); + } + + /* The entire point is to reserve the inputs. */ + json_add_bool(req->js, "reserve", true); + /* How much do we need to reserve? */ + if (has_all(mfc)) + json_add_string(req->js, "satoshi", "all"); + else { + struct amount_sat sum = AMOUNT_SAT(0); + for (size_t i = 0; i < tal_count(mfc->destinations); ++i) { + if (!amount_sat_add(&sum, + sum, mfc->destinations[i].amount)) + return mfc_fail(mfc, JSONRPC2_INVALID_PARAMS, + "Overflow while summing " + "destination values."); + } + json_add_string(req->js, "satoshi", + type_to_string(tmpctx, struct amount_sat, + &sum)); + } + json_add_string(req->js, "feerate", + mfc->feerate_str ? mfc->feerate_str : "normal"); + + { + size_t startweight; + size_t num_outs = tal_count(mfc->destinations); + /* Assume 1 input. + * As long as lightningd does not select more than 252 + * inputs, that estimation should be correct. + */ + startweight = bitcoin_tx_core_weight(1, num_outs) + + ( bitcoin_tx_output_weight( + BITCOIN_SCRIPTPUBKEY_P2WSH_LEN) + * num_outs + ); + json_add_string(req->js, "startweight", + tal_fmt(tmpctx, "%zu", startweight)); + } + + /* If we've got v2 opens, we need to use a min weight of 110. */ + /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #3: + * The minimum witness weight for an input is 110. + */ + if (dest_count(mfc, OPEN_CHANNEL) > 0) { + json_add_string(req->js, "min_witness_weight", + tal_fmt(tmpctx, "%u", 110)); + } + + + return send_outreq(mfc->cmd->plugin, req); +} + + + /*----------------------------------------------------------------------------- Starting -----------------------------------------------------------------------------*/ @@ -1819,6 +1802,22 @@ redo_multifundchannel(struct multifundchannel_command *mfc, /*----------------------------------------------------------------------------- Command Entry Point -----------------------------------------------------------------------------*/ +static struct command_result * +param_positive_number(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + unsigned int **num) +{ + struct command_result *res = param_number(cmd, name, buffer, tok, num); + if (res) + return res; + if (**num == 0) + return command_fail_badparam(cmd, name, buffer, tok, + "should be a positive integer"); + return NULL; +} + /* Entry function. */ static struct command_result *