rgb-cln/lightningd/dual_open_control.c

3744 lines
112 KiB
C
Raw Normal View History

/* This is the lightningd handler for messages to/from various
* dualopend subdaemons. It manages the callbacks and database
* saves and funding tx watching for a channel open */
#include "config.h"
#include <bitcoin/short_channel_id.h>
#include <ccan/array_size/array_size.h>
#include <ccan/cast/cast.h>
#include <ccan/mem/mem.h>
#include <ccan/tal/str/str.h>
#include <common/blockheight_states.h>
#include <common/json_command.h>
#include <common/json_param.h>
#include <common/psbt_open.h>
#include <common/shutdown_scriptpubkey.h>
#include <common/type_to_string.h>
#include <common/wire_error.h>
#include <connectd/connectd_wiregen.h>
#include <errno.h>
#include <hsmd/capabilities.h>
#include <lightningd/chaintopology.h>
#include <lightningd/channel.h>
#include <lightningd/channel_control.h>
#include <lightningd/closing_control.h>
#include <lightningd/connect_control.h>
#include <lightningd/dual_open_control.h>
#include <lightningd/gossip_control.h>
#include <lightningd/hsm_control.h>
#include <lightningd/notification.h>
#include <lightningd/opening_common.h>
#include <lightningd/peer_control.h>
2022-01-11 01:13:59 +00:00
#include <lightningd/peer_fd.h>
#include <lightningd/plugin_hook.h>
#include <openingd/dualopend_wiregen.h>
#include <sodium/randombytes.h>
struct commit_rcvd {
struct channel *channel;
struct channel_id cid;
struct uncommitted_channel *uc;
};
static void channel_disconnect(struct channel *channel,
enum log_level level,
bool reconnect,
const char *desc)
{
log_(channel->log, level, NULL, false, "%s", desc);
channel_cleanup_commands(channel, desc);
channel_fail_transient(channel, "%s: %s",
channel->owner ?
channel->owner->name :
"dualopend-dead",
desc);
}
void channel_unsaved_close_conn(struct channel *channel, const char *why)
{
/* Gotta be unsaved */
assert(channel_unsaved(channel));
log_info(channel->log, "Unsaved peer failed."
" Disconnecting and deleting channel. Reason: %s",
why);
channel_cleanup_commands(channel, why);
assert(channel->owner);
channel_set_owner(channel, NULL);
delete_channel(channel);
}
static void channel_saved_err_broken_reconn(struct channel *channel,
const char *fmt, ...)
{
va_list ap;
const char *errmsg;
/* We only reconnect to 'saved' channel peers */
assert(!channel_unsaved(channel));
va_start(ap, fmt);
errmsg = tal_vfmt(tmpctx, fmt, ap);
va_end(ap);
log_broken(channel->log, "%s", errmsg);
channel_disconnect(channel, LOG_INFORM, true, errmsg);
}
static void channel_err_broken(struct channel *channel,
const char *fmt, ...)
{
va_list ap;
const char *errmsg;
va_start(ap, fmt);
errmsg = tal_vfmt(tmpctx, fmt, ap);
va_end(ap);
if (channel_unsaved(channel)) {
log_broken(channel->log, "%s", errmsg);
channel_unsaved_close_conn(channel, errmsg);
} else
channel_disconnect(channel, LOG_BROKEN, false, errmsg);
}
void json_add_unsaved_channel(struct json_stream *response,
const struct channel *channel,
/* Only set for listpeerchannels */
const struct peer *peer)
{
struct amount_msat total;
struct open_attempt *oa;
if (!channel)
return;
/* If we're chatting but no channel, that's shown by connected: True */
if (!channel->open_attempt)
return;
/* If we're calling out to connectd to activate peer to start the
* process, this will be NULL */
if (!channel->owner)
return;
oa = channel->open_attempt;
json_object_start(response, NULL);
/* listpeerchannels only */
if (peer) {
json_add_node_id(response, "peer_id", &peer->id);
json_add_bool(response, "peer_connected", peer->connected == PEER_CONNECTED);
}
json_add_string(response, "state", channel_state_name(channel));
json_add_string(response, "owner", channel->owner->name);
json_add_string(response, "opener", channel->opener == LOCAL ?
"local" : "remote");
json_array_start(response, "status");
for (size_t i = 0; i < ARRAY_SIZE(channel->billboard.permanent); i++) {
if (!channel->billboard.permanent[i])
continue;
json_add_string(response, NULL,
channel->billboard.permanent[i]);
}
if (channel->billboard.transient)
json_add_string(response, NULL, channel->billboard.transient);
json_array_end(response);
/* funding + our_upfront_shutdown only available if we're initiator */
if (oa->role == TX_INITIATOR) {
if (amount_sat_to_msat(&total, oa->funding)) {
json_add_amount_msat(response, "to_us_msat", total);
/* This will change if peer adds funds */
json_add_amount_msat(response, "total_msat", total);
}
}
json_array_start(response, "features");
/* v2 channels assume static_remotekey */
json_add_string(response, NULL, "option_static_remotekey");
if (feature_negotiated(channel->peer->ld->our_features,
channel->peer->their_features,
OPT_ANCHOR_OUTPUTS))
json_add_string(response, NULL, "option_anchor_outputs");
json_array_end(response);
json_object_end(response);
}
struct rbf_channel_payload {
struct subd *dualopend;
struct channel *channel;
struct node_id peer_id;
/* Info specific to this RBF */
struct channel_id channel_id;
struct amount_sat their_last_funding;
struct amount_sat their_proposed_funding;
struct amount_sat our_last_funding;
u32 funding_feerate_per_kw;
u32 locktime;
bool req_confirmed_ins_remote;
/* General info */
u32 feerate_our_max;
u32 feerate_our_min;
/* What's the maximum amount of funding
* this channel can hold */
struct amount_sat channel_max;
/* If they've requested funds, this is their request */
struct amount_sat *requested_lease_amt;
/* Returned from hook */
struct amount_sat our_funding;
struct wally_psbt *psbt;
char *err_msg;
};
static void rbf_channel_hook_serialize(struct rbf_channel_payload *payload,
struct json_stream *stream,
struct plugin *plugin)
{
json_object_start(stream, "rbf_channel");
json_add_node_id(stream, "id", &payload->peer_id);
json_add_channel_id(stream, "channel_id", &payload->channel_id);
json_add_amount_sat_msat(stream, "their_last_funding_msat",
payload->their_last_funding);
json_add_amount_sat_msat(stream, "their_funding_msat",
payload->their_proposed_funding);
json_add_amount_sat_msat(stream, "our_last_funding_msat",
payload->our_last_funding);
json_add_num(stream, "locktime", payload->locktime);
json_add_num(stream, "feerate_our_max",
payload->feerate_our_max);
json_add_num(stream, "feerate_our_min",
payload->feerate_our_min);
json_add_num(stream, "funding_feerate_per_kw",
payload->funding_feerate_per_kw);
json_add_amount_sat_msat(stream, "channel_max_msat",
payload->channel_max);
if (payload->requested_lease_amt)
json_add_amount_sat_msat(stream, "requested_lease_msat",
*payload->requested_lease_amt);
json_add_bool(stream, "require_confirmed_inputs",
payload->req_confirmed_ins_remote);
json_object_end(stream);
}
/* ~Map of the Territory~
*
* openchannel hook
- reserveinputs feerate [{"amt": amt, "script": ""}] excludecommon=true -> psbt
-> psbt_set
*
* openchannel_changed hook
- psbt --> most recent
-> psbt_set (if same as orig) | complete flag
*
* openchannel_sign hook
- signpsbt psbt -> partially_signed_psbt
-> partially_signed_psbt
*/
struct openchannel2_payload {
struct subd *dualopend;
struct channel *channel;
struct node_id peer_id;
struct channel_id channel_id;
struct amount_sat their_funding;
struct amount_sat dust_limit_satoshis;
struct amount_msat max_htlc_value_in_flight_msat;
struct amount_msat htlc_minimum_msat;
u32 funding_feerate_per_kw;
u32 feerate_our_max;
u32 feerate_our_min;
u32 commitment_feerate_per_kw;
u16 to_self_delay;
u16 max_accepted_htlcs;
u8 channel_flags;
u32 locktime;
u8 *shutdown_scriptpubkey;
/* What's the maximum amount of funding
* this channel can hold */
struct amount_sat channel_max;
/* If they've requested funds, this is their request */
struct amount_sat *requested_lease_amt;
u32 lease_blockheight_start;
u32 node_blockheight;
bool req_confirmed_ins_remote;
struct amount_sat accepter_funding;
struct wally_psbt *psbt;
const u8 *our_shutdown_scriptpubkey;
struct lease_rates *rates;
char *err_msg;
};
static void openchannel2_hook_serialize(struct openchannel2_payload *payload,
struct json_stream *stream,
struct plugin *plugin)
{
json_object_start(stream, "openchannel2");
json_add_node_id(stream, "id", &payload->peer_id);
json_add_channel_id(stream, "channel_id", &payload->channel_id);
json_add_amount_sat_msat(stream,
"their_funding_msat", payload->their_funding);
json_add_amount_sat_msat(stream,
"dust_limit_msat", payload->dust_limit_satoshis);
json_add_amount_msat(stream, "max_htlc_value_in_flight_msat",
payload->max_htlc_value_in_flight_msat);
json_add_amount_msat(stream, "htlc_minimum_msat",
payload->htlc_minimum_msat);
json_add_num(stream, "funding_feerate_per_kw",
payload->funding_feerate_per_kw);
json_add_num(stream, "commitment_feerate_per_kw",
payload->commitment_feerate_per_kw);
json_add_num(stream, "feerate_our_max",
payload->feerate_our_max);
json_add_num(stream, "feerate_our_min",
payload->feerate_our_min);
json_add_num(stream, "to_self_delay", payload->to_self_delay);
json_add_num(stream, "max_accepted_htlcs", payload->max_accepted_htlcs);
json_add_num(stream, "channel_flags", payload->channel_flags);
json_add_num(stream, "locktime", payload->locktime);
if (tal_bytelen(payload->shutdown_scriptpubkey) != 0)
json_add_hex_talarr(stream, "shutdown_scriptpubkey",
payload->shutdown_scriptpubkey);
json_add_amount_sat_msat(stream, "channel_max_msat",
payload->channel_max);
if (payload->requested_lease_amt) {
json_add_amount_sat_msat(stream, "requested_lease_msat",
*payload->requested_lease_amt);
json_add_num(stream, "lease_blockheight_start",
payload->lease_blockheight_start);
json_add_num(stream, "node_blockheight",
payload->node_blockheight);
}
json_add_bool(stream, "require_confirmed_inputs",
payload->req_confirmed_ins_remote);
json_object_end(stream);
}
struct openchannel2_psbt_payload {
struct subd *dualopend;
struct wally_psbt *psbt;
struct channel *channel;
struct lightningd *ld;
};
static void
openchannel2_changed_hook_serialize(struct openchannel2_psbt_payload *payload,
struct json_stream *stream,
struct plugin *plugin)
{
json_object_start(stream, "openchannel2_changed");
json_add_psbt(stream, "psbt", payload->psbt);
json_add_string(stream, "channel_id",
type_to_string(tmpctx, struct channel_id,
&payload->channel->cid));
json_add_bool(stream, "require_confirmed_inputs",
payload->channel->req_confirmed_ins[REMOTE]);
json_object_end(stream);
}
static void
openchannel2_sign_hook_serialize(struct openchannel2_psbt_payload *payload,
struct json_stream *stream,
struct plugin *plugin)
{
json_object_start(stream, "openchannel2_sign");
json_add_psbt(stream, "psbt", payload->psbt);
json_add_string(stream, "channel_id",
type_to_string(tmpctx, struct channel_id,
&payload->channel->cid));
json_object_end(stream);
}
static const u8 *hook_extract_shutdown_script(struct subd* dualopend,
const char *buffer,
const jsmntok_t *toks)
{
const u8 *close_to_script;
enum address_parse_result parse_res;
if (!buffer)
return NULL;
const jsmntok_t *t = json_get_member(buffer, toks, "result");
if (!t)
fatal("Plugin must return a 'result'"
"%.*s", toks[0].end - toks[0].start,
buffer + toks[0].start);
if (!json_tok_streq(buffer, t, "continue")) {
char *errmsg = "Client error. Unable to continue";
subd_send_msg(dualopend,
take(towire_dualopend_fail(NULL, errmsg)));
return NULL;
}
const jsmntok_t *close_to_tok = json_get_member(buffer, toks, "close_to");
if (!close_to_tok)
return NULL;
parse_res = json_to_address_scriptpubkey(tmpctx, chainparams, buffer,
close_to_tok, &close_to_script);
switch (parse_res) {
case ADDRESS_PARSE_UNRECOGNIZED:
fatal("Plugin returned an invalid response to the"
" openchannel2.close_to hook: %.*s",
t->end - t->start, buffer + t->start);
case ADDRESS_PARSE_WRONG_NETWORK:
fatal("Plugin returned invalid response to the"
" openchannel2.close_to hook: address %s is"
" not on network %s",
tal_hex(NULL, close_to_script),
chainparams->network_name);
case ADDRESS_PARSE_SUCCESS:
return close_to_script;
}
return NULL;
}
static bool
hook_extract_psbt(const tal_t *ctx, struct subd *dualopend, const char *buffer,
const jsmntok_t *toks, char *hook_name,
bool allow_empty,
struct wally_psbt **out)
{
if (!buffer)
fatal("Plugin must return a valid response to %s", hook_name);
const jsmntok_t *t = json_get_member(buffer, toks, "result");
if (!t)
fatal("Plugin must return a 'result' to %s"
"%.*s", hook_name, toks[0].end - toks[0].start,
buffer + toks[0].start);
if (!json_tok_streq(buffer, t, "continue")) {
/* dualopend might have closed if we're on the signed round */
if (dualopend) {
char *errmsg = "Client error. Unable to continue";
subd_send_msg(dualopend,
take(towire_dualopend_fail(NULL, errmsg)));
}
return false;
}
const jsmntok_t *psbt_tok = json_get_member(buffer, toks, "psbt");
if (!psbt_tok) {
if (!allow_empty)
fatal("Plugin must return a 'psbt' to a 'continue'd"
"%s %.*s", hook_name,
toks[0].end - toks[0].start,
buffer + toks[0].start);
*out = NULL;
return true;
}
*out = json_to_psbt(ctx, buffer, psbt_tok);
if (!*out)
fatal("Plugin must return a valid 'psbt' to a 'continue'd"
"%s %.*s", hook_name,
toks[0].end - toks[0].start,
buffer + toks[0].start);
return true;
}
/* The field is *always* assumed msats, as that's the unit
* amount we're transitioning our API over to. A 'xxxsat'
* unit will be interpreted correctly, but a value given
* without a unit will always be interpreted as msats */
static bool
hook_extract_amount(struct subd *dualopend,
const char *buffer,
const jsmntok_t *toks,
char *field_name,
struct amount_sat *amount)
{
struct amount_msat msats;
if (!buffer)
return false;
const jsmntok_t *t = json_get_member(buffer, toks, "result");
if (!t)
fatal("Plugin must return a 'result' "
" %.*s", toks[0].end - toks[0].start,
buffer + toks[0].start);
if (!json_tok_streq(buffer, t, "continue")) {
char *errmsg = "Client error. Unable to continue";
subd_send_msg(dualopend,
take(towire_dualopend_fail(NULL, errmsg)));
return false;
}
/* If there's no amount_sat field, that's ok */
const jsmntok_t *amt_tok = json_get_member(buffer, toks, field_name);
if (!amt_tok) {
*amount = AMOUNT_SAT(0);
return true;
}
if (!json_to_msat(buffer, amt_tok, &msats))
fatal("Plugin must return a valid '%s' to a 'continue'd"
" %.*s", field_name,
toks[0].end - toks[0].start,
buffer + toks[0].start);
*amount = amount_msat_to_sat_round_down(msats);
return true;
}
static void rbf_channel_remove_dualopend(struct subd *dualopend,
struct rbf_channel_payload *payload)
{
assert(payload->dualopend == dualopend);
payload->dualopend = NULL;
}
static void rbf_channel_hook_cb(struct rbf_channel_payload *payload STEALS)
{
struct subd *dualopend = payload->dualopend;
struct channel *channel = payload->channel;
u8 *msg;
tal_steal(tmpctx, payload);
if (!dualopend)
return;
tal_del_destructor2(dualopend, rbf_channel_remove_dualopend, payload);
if (channel->state != DUALOPEND_AWAITING_LOCKIN) {
log_debug(channel->log,
"rbf_channel hook returned, but channel in state"
" %s", channel_state_name(channel));
msg = towire_dualopend_fail(NULL, "Peer error. Channel"
" not ready for RBF attempt.");
return subd_send_msg(dualopend, take(msg));
}
if (payload->err_msg) {
log_debug(channel->log,
"rbf_channel hook rejects and says '%s'",
payload->err_msg);
msg = towire_dualopend_fail(NULL, payload->err_msg);
return subd_send_msg(dualopend, take(msg));
}
/* Update channel with new open attempt. */
channel->open_attempt = new_channel_open_attempt(channel);
msg = towire_dualopend_got_rbf_offer_reply(NULL,
payload->our_funding,
payload->psbt);
subd_send_msg(dualopend, take(msg));
}
static bool
rbf_channel_hook_deserialize(struct rbf_channel_payload *payload,
const char *buffer,
const jsmntok_t *toks)
{
struct subd *dualopend = payload->dualopend;
struct channel *channel = payload->channel;
if (!dualopend) {
rbf_channel_hook_cb(payload);
return false;
}
/* FIXME: move to new json extraction */
const jsmntok_t *t_result = json_get_member(buffer, toks, "result");
if (!t_result)
fatal("Plugin returned an invalid response to the"
" rbf_channel hook: %.*s",
json_tok_full_len(toks),
json_tok_full(buffer, toks));
if (json_tok_streq(buffer, t_result, "reject")) {
if (json_get_member(buffer, toks, "psbt"))
fatal("Plugin rejected rbf_channel but"
" also set `psbt`");
if (json_get_member(buffer, toks, "our_funding_msat"))
fatal("Plugin rejected rbf_channel but"
" also set `our_funding_msat`");
const jsmntok_t *t_errmsg = json_get_member(buffer, toks,
"error_message");
if (t_errmsg)
payload->err_msg = json_strdup(payload, buffer,
t_errmsg);
else
payload->err_msg = "";
rbf_channel_hook_cb(payload);
return false;
} else if (!json_tok_streq(buffer, t_result, "continue"))
fatal("Plugin returned invalid response to rbf_channel hook:"
" %.*s", json_tok_full_len(toks),
json_tok_full(buffer, toks));
if (!hook_extract_psbt(payload, dualopend, buffer, toks,
"rbf_channel", true, &payload->psbt))
return false;
if (payload->psbt) {
enum tx_role our_role = channel->opener == LOCAL ?
TX_INITIATOR : TX_ACCEPTER;
psbt_add_serials(payload->psbt, our_role);
}
/* We require the PSBT to meet certain criteria such as
* extra, proprietary fields (`serial_id`s) or
* to have a `redeemscripts` iff the inputs are P2SH.
*
* Since this is externally provided, we confirm that
* they've done the right thing / haven't lost any required info.
*/
if (payload->psbt && !psbt_has_required_fields(payload->psbt))
fatal("Plugin supplied PSBT that's missing"
" required fields: %s",
type_to_string(tmpctx, struct wally_psbt,
payload->psbt));
if (!hook_extract_amount(dualopend, buffer, toks,
"our_funding_msat", &payload->our_funding))
fatal("Plugin failed to supply our_funding_msat field");
if (payload->psbt
&& amount_sat_zero(payload->our_funding))
fatal("Plugin failed to supply our_funding_msat field");
if (!payload->psbt &&
!amount_sat_zero(payload->our_funding)) {
log_broken(channel->log, "`our_funding_msat` returned"
" but no `psbt` present. %.*s",
json_tok_full_len(toks),
json_tok_full(buffer, toks));
payload->err_msg = "Client error. Unable to continue";
rbf_channel_hook_cb(payload);
return false;
}
return true;
}
/* dualopend dies? Remove dualopend ptr from payload */
static void openchannel2_remove_dualopend(struct subd *dualopend,
struct openchannel2_payload *payload)
{
assert(payload->dualopend == dualopend);
payload->dualopend = NULL;
}
static void
openchannel2_hook_cb(struct openchannel2_payload *payload STEALS)
{
struct subd *dualopend = payload->dualopend;
struct channel *channel = payload->channel;
2021-11-04 17:48:43 +00:00
u32 *our_shutdown_script_wallet_index;
u8 *msg;
/* Our daemon died! */
if (!dualopend)
return;
df: patch for valgrind error We were freeing the payload, which is then subsequently freed by the plugin_hook caller. Whoops. Now we pass through to the callback function and just clean up neatly. ------------------------------- Valgrind errors -------------------------------- Valgrind error file: valgrind-errors.406602 ==406602== Invalid read of size 8 ==406602== at 0x12AC93: openchannel2_hook_cb (dual_open_control.c:669) ==406602== by 0x12AF0A: openchannel2_hook_deserialize (dual_open_control.c:721) ==406602== by 0x16EF0E: plugin_hook_callback (plugin_hook.c:186) ==406602== by 0x169746: plugin_response_handle (plugin.c:514) ==406602== by 0x169959: plugin_read_json_one (plugin.c:620) ==406602== by 0x169B23: plugin_read_json (plugin.c:665) ==406602== by 0x1F4076: next_plan (io.c:59) ==406602== by 0x1F4C5B: do_plan (io.c:407) ==406602== by 0x1F4C9D: io_ready (io.c:417) ==406602== by 0x1F6F35: io_loop (poll.c:445) ==406602== by 0x13D48D: io_loop_with_timers (io_loop_with_timers.c:24) ==406602== by 0x143388: main (lightningd.c:1111) ==406602== Address 0x75e7418 is 56 bytes inside a block of size 3,520 free'd ==406602== at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==406602== by 0x204FB0: del_tree (tal.c:421) ==406602== by 0x20527E: tal_free (tal.c:486) ==406602== by 0x122D68: delete_channel (channel.c:124) ==406602== by 0x129291: channel_disconnect (dual_open_control.c:63) ==406602== by 0x129364: channel_close_conn (dual_open_control.c:82) ==406602== by 0x131CF6: peer_please_disconnect (connect_control.c:304) ==406602== by 0x131DEB: connectd_msg (connect_control.c:326) ==406602== by 0x172023: sd_msg_read (subd.c:509) ==406602== by 0x1F4076: next_plan (io.c:59) ==406602== by 0x1F4C5B: do_plan (io.c:407) ==406602== by 0x1F4C9D: io_ready (io.c:417) ==406602== Block was alloc'd at ==406602== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==406602== by 0x204A39: allocate (tal.c:250) ==406602== by 0x204FFA: tal_alloc_ (tal.c:428) ==406602== by 0x123165: new_unsaved_channel (channel.c:209) ==406602== by 0x130D34: peer_start_dualopend (dual_open_control.c:2985) ==406602== by 0x15BD2A: peer_connected_hook_final (peer_control.c:1105) ==406602== by 0x16F2E5: plugin_hook_call_ (plugin_hook.c:275) ==406602== by 0x15BF5C: plugin_hook_call_peer_connected (peer_control.c:1155) ==406602== by 0x15C16C: peer_connected (peer_control.c:1208) ==406602== by 0x131E3B: connectd_msg (connect_control.c:332) ==406602== by 0x172023: sd_msg_read (subd.c:509) ==406602== by 0x171842: read_fds (subd.c:310)
2021-05-10 20:06:51 +01:00
/* Free payload regardless of what happens next */
tal_steal(tmpctx, payload);
channel = dualopend->channel;
/* Channel open is currently in progress elsewhere! */
if (channel->open_attempt) {
msg = towire_dualopend_fail(NULL, "Already initiated channel"
" open");
log_debug(dualopend->ld->log,
"Our open in progress, denying their offer");
return subd_send_msg(dualopend, take(msg));
}
tal_del_destructor2(dualopend, openchannel2_remove_dualopend, payload);
if (payload->err_msg) {
log_debug(dualopend->ld->log,
"openchannel2 hook rejects and says '%s'",
payload->err_msg);
msg = towire_dualopend_fail(NULL, payload->err_msg);
return subd_send_msg(dualopend, take(msg));
}
2021-11-04 17:48:43 +00:00
/* Determine the wallet index for our_shutdown_scriptpubkey,
* NULL if not found. */
u32 found_wallet_index;
bool is_p2sh;
if (wallet_can_spend(dualopend->ld->wallet,
payload->our_shutdown_scriptpubkey,
&found_wallet_index,
&is_p2sh)) {
our_shutdown_script_wallet_index = tal(tmpctx, u32);
*our_shutdown_script_wallet_index = found_wallet_index;
} else
our_shutdown_script_wallet_index = NULL;
channel->cid = payload->channel_id;
channel->opener = REMOTE;
channel->open_attempt = new_channel_open_attempt(channel);
channel->req_confirmed_ins[REMOTE] =
payload->req_confirmed_ins_remote;
msg = towire_dualopend_got_offer_reply(NULL,
payload->accepter_funding,
payload->psbt,
payload->our_shutdown_scriptpubkey,
2021-11-04 17:48:43 +00:00
our_shutdown_script_wallet_index,
payload->rates);
subd_send_msg(dualopend, take(msg));
}
static bool
openchannel2_hook_deserialize(struct openchannel2_payload *payload,
const char *buffer,
const jsmntok_t *toks)
{
const u8 *shutdown_script;
const char *err;
struct subd *dualopend = payload->dualopend;
/* If our daemon died, we're done */
if (!dualopend) {
df: patch for valgrind error We were freeing the payload, which is then subsequently freed by the plugin_hook caller. Whoops. Now we pass through to the callback function and just clean up neatly. ------------------------------- Valgrind errors -------------------------------- Valgrind error file: valgrind-errors.406602 ==406602== Invalid read of size 8 ==406602== at 0x12AC93: openchannel2_hook_cb (dual_open_control.c:669) ==406602== by 0x12AF0A: openchannel2_hook_deserialize (dual_open_control.c:721) ==406602== by 0x16EF0E: plugin_hook_callback (plugin_hook.c:186) ==406602== by 0x169746: plugin_response_handle (plugin.c:514) ==406602== by 0x169959: plugin_read_json_one (plugin.c:620) ==406602== by 0x169B23: plugin_read_json (plugin.c:665) ==406602== by 0x1F4076: next_plan (io.c:59) ==406602== by 0x1F4C5B: do_plan (io.c:407) ==406602== by 0x1F4C9D: io_ready (io.c:417) ==406602== by 0x1F6F35: io_loop (poll.c:445) ==406602== by 0x13D48D: io_loop_with_timers (io_loop_with_timers.c:24) ==406602== by 0x143388: main (lightningd.c:1111) ==406602== Address 0x75e7418 is 56 bytes inside a block of size 3,520 free'd ==406602== at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==406602== by 0x204FB0: del_tree (tal.c:421) ==406602== by 0x20527E: tal_free (tal.c:486) ==406602== by 0x122D68: delete_channel (channel.c:124) ==406602== by 0x129291: channel_disconnect (dual_open_control.c:63) ==406602== by 0x129364: channel_close_conn (dual_open_control.c:82) ==406602== by 0x131CF6: peer_please_disconnect (connect_control.c:304) ==406602== by 0x131DEB: connectd_msg (connect_control.c:326) ==406602== by 0x172023: sd_msg_read (subd.c:509) ==406602== by 0x1F4076: next_plan (io.c:59) ==406602== by 0x1F4C5B: do_plan (io.c:407) ==406602== by 0x1F4C9D: io_ready (io.c:417) ==406602== Block was alloc'd at ==406602== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==406602== by 0x204A39: allocate (tal.c:250) ==406602== by 0x204FFA: tal_alloc_ (tal.c:428) ==406602== by 0x123165: new_unsaved_channel (channel.c:209) ==406602== by 0x130D34: peer_start_dualopend (dual_open_control.c:2985) ==406602== by 0x15BD2A: peer_connected_hook_final (peer_control.c:1105) ==406602== by 0x16F2E5: plugin_hook_call_ (plugin_hook.c:275) ==406602== by 0x15BF5C: plugin_hook_call_peer_connected (peer_control.c:1155) ==406602== by 0x15C16C: peer_connected (peer_control.c:1208) ==406602== by 0x131E3B: connectd_msg (connect_control.c:332) ==406602== by 0x172023: sd_msg_read (subd.c:509) ==406602== by 0x171842: read_fds (subd.c:310)
2021-05-10 20:06:51 +01:00
openchannel2_hook_cb(payload);
return false;
}
const jsmntok_t *t_result = json_get_member(buffer, toks, "result");
if (!t_result)
fatal("Plugin returned an invalid response to the"
" openchannel2 hook: %.*s",
json_tok_full_len(toks),
json_tok_full(buffer, toks));
if (json_tok_streq(buffer, t_result, "reject")) {
/* Should not have set any other fields if 'reject'ing */
if (json_get_member(buffer, toks, "close_to"))
fatal("Plugin rejected openchannel2 but"
" also set close_to");
if (json_get_member(buffer, toks, "psbt"))
fatal("Plugin rejected openchannel2 but"
" also set `psbt`");
if (json_get_member(buffer, toks, "our_funding_msat"))
fatal("Plugin rejected openchannel2 but"
" also set `our_funding_psbt`");
const jsmntok_t *t_errmsg = json_get_member(buffer, toks,
"error_message");
if (t_errmsg)
payload->err_msg = json_strdup(payload,
buffer, t_errmsg);
else
payload->err_msg = "";
openchannel2_hook_cb(payload);
return false;
} else if (!json_tok_streq(buffer, t_result, "continue"))
fatal("Plugin returned an invalid response to the"
" openchannel2 hook: %.*s",
json_tok_full_len(toks),
json_tok_full(buffer, toks));
if (!hook_extract_psbt(payload, dualopend, buffer, toks,
"openchannel2", true, &payload->psbt))
return false;
shutdown_script =
hook_extract_shutdown_script(dualopend, buffer, toks);
if (shutdown_script && payload->our_shutdown_scriptpubkey)
log_broken(dualopend->ld->log,
"openchannel2 hook close_to address was"
" already set by other plugin. Ignoring!");
else
payload->our_shutdown_scriptpubkey = shutdown_script;
struct amount_msat fee_base, fee_max_base;
/* deserialized may be called multiple times */
if (!payload->rates)
payload->rates = tal(payload, struct lease_rates);
err = json_scan(payload, buffer, toks,
"{lease_fee_base_msat:%"
",lease_fee_basis:%"
",channel_fee_max_base_msat:%"
",channel_fee_max_proportional_thousandths:%"
",funding_weight:%}",
JSON_SCAN(json_to_msat, &fee_base),
JSON_SCAN(json_to_u16,
&payload->rates->lease_fee_basis),
JSON_SCAN(json_to_msat, &fee_max_base),
JSON_SCAN(json_to_u16,
&payload->rates->channel_fee_max_proportional_thousandths),
JSON_SCAN(json_to_u16,
&payload->rates->funding_weight));
/* It's possible they didn't send these back! */
if (err)
payload->rates = tal_free(payload->rates);
/* Convert to u32s */
if (payload->rates &&
!lease_rates_set_lease_fee_msat(payload->rates, fee_base))
fatal("Plugin sent overflowing/non-sat `lease_fee_base_msat`");
if (payload->rates &&
!lease_rates_set_chan_fee_base_msat(payload->rates, fee_max_base))
fatal("Plugin sent overflowing `channel_fee_max_base_msat`");
/* Add a serial_id to everything that doesn't have one yet */
if (payload->psbt)
psbt_add_serials(payload->psbt, TX_ACCEPTER);
/* We require the PSBT to meet certain criteria such as
* extra, proprietary fields (`serial_id`s) or
* to have a `redeemscripts` iff the inputs are P2SH.
*
* Since this is externally provided, we confirm that
* they've done the right thing / haven't lost any required info.
*/
if (payload->psbt && !psbt_has_required_fields(payload->psbt))
fatal("Plugin supplied PSBT that's missing required fields. %s",
type_to_string(tmpctx, struct wally_psbt, payload->psbt));
if (!hook_extract_amount(dualopend, buffer, toks,
"our_funding_msat",
&payload->accepter_funding))
fatal("Plugin failed to supply our_funding_msat field");
if (payload->psbt
&& amount_sat_zero(payload->accepter_funding))
fatal("Plugin failed to supply our_funding_msat field");
if (!payload->psbt
&& !amount_sat_zero(payload->accepter_funding)) {
/* Gotta give a PSBT if you set the accepter_funding amount */
/* Let dualopend know we've failed */
payload->err_msg = "Client error. Unable to continue";
openchannel2_hook_cb(payload);
return false;
}
return true;
}
/* dualopend dies? Remove dualopend ptr from payload */
2020-11-24 00:36:22 +00:00
static void
openchannel2_psbt_remove_dualopend(struct subd *dualopend,
struct openchannel2_psbt_payload *payload)
{
assert(payload->dualopend == dualopend);
payload->dualopend = NULL;
}
static bool
openchannel2_changed_deserialize(struct openchannel2_psbt_payload *payload,
const char *buffer, const jsmntok_t *toks)
{
struct subd *dualopend = payload->dualopend;
struct wally_psbt *psbt;
if (!hook_extract_psbt(NULL, dualopend, buffer,
toks, "openchannel2_changed",
false, &psbt))
return false;
/* Add serials to PSBT, before checking for required fields */
psbt_add_serials(psbt, TX_ACCEPTER);
/* We require the PSBT to meet certain criteria such as
* extra, proprietary fields (`serial_id`s) or
* to have a `redeemscripts` iff the inputs are P2SH.
*
* Since this is externally provided, we confirm that
* they've done the right thing / haven't lost any required info.
*/
if (!psbt_has_required_fields(psbt))
fatal("Plugin supplied PSBT that's missing required fields. %s",
type_to_string(tmpctx, struct wally_psbt, psbt));
if (payload->psbt)
tal_free(payload->psbt);
payload->psbt = tal_steal(payload, psbt);
return true;
}
static void
openchannel2_changed_hook_cb(struct openchannel2_psbt_payload *payload STEALS)
{
struct subd *dualopend = payload->dualopend;
/* Free payload regardless of what happens next */
tal_steal(tmpctx, payload);
/* If our daemon died, we're done */
if (!dualopend)
return;
tal_del_destructor2(dualopend,
openchannel2_psbt_remove_dualopend,
payload);
subd_send_msg(dualopend,
take(towire_dualopend_psbt_updated(NULL,
payload->psbt)));
}
static bool
openchannel2_signed_deserialize(struct openchannel2_psbt_payload *payload,
const char *buffer, const jsmntok_t *toks)
{
struct subd *dualopend = payload->dualopend;
struct wally_psbt *psbt;
if (!hook_extract_psbt(NULL, dualopend, buffer,
toks, "openchannel2_sign",
false, &psbt))
return false;
/* We require the PSBT to meet certain criteria such as
* extra, proprietary fields (`serial_id`s) or
* to have a `redeemscripts` iff the inputs are P2SH.
*
* Since this is externally provided, we confirm that
* they've done the right thing / haven't lost any required info.
*/
if (!psbt_has_required_fields(psbt))
fatal("Plugin supplied PSBT that's missing required fields. %s",
type_to_string(tmpctx, struct wally_psbt, psbt));
/* Verify that inputs/outputs are the same. Note that this is a
* 'de minimus' check -- we just look at serial_ids. If you've
* totally managled the data here but left the serial_ids intact,
* you'll get a failure back from the peer when you send
* commitment sigs */
if (psbt_contribs_changed(payload->psbt, psbt))
fatal("Plugin must not change psbt input/output set. "
"orig: %s. updated: %s",
type_to_string(tmpctx, struct wally_psbt,
payload->psbt),
type_to_string(tmpctx, struct wally_psbt,
psbt));
if (payload->psbt)
tal_free(payload->psbt);
payload->psbt = tal_steal(payload, psbt);
return true;
}
static void
openchannel2_sign_hook_cb(struct openchannel2_psbt_payload *payload STEALS)
{
struct channel *channel = payload->channel;
struct channel_inflight *inflight;
struct bitcoin_txid txid;
u8 *msg;
/* Whatever happens, we free the payload */
tal_steal(tmpctx, payload);
/* Finalize it, if not already. It shouldn't work entirely */
psbt_finalize(payload->psbt);
if (!psbt_side_finalized(payload->psbt, TX_ACCEPTER)) {
log_broken(channel->log,
"Plugin must return a 'psbt' with signatures "
"for their inputs %s",
type_to_string(tmpctx,
struct wally_psbt,
payload->psbt));
msg = towire_dualopend_fail(NULL, "Peer error with PSBT"
" signatures.");
goto send_msg;
}
inflight = channel_current_inflight(channel);
if (!inflight) {
log_broken(channel->log,
"No current channel inflight");
msg = towire_dualopend_fail(NULL, "No current channel inflight");
goto send_msg;
}
/* Check that we've got the same / correct PSBT */
psbt_txid(NULL, payload->psbt, &txid, NULL);
if (!bitcoin_txid_eq(&inflight->funding->outpoint.txid, &txid)) {
log_broken(channel->log,
"PSBT's txid does not match. %s != %s",
type_to_string(tmpctx, struct bitcoin_txid,
&txid),
type_to_string(tmpctx, struct bitcoin_txid,
&inflight->funding->outpoint.txid));
msg = towire_dualopend_fail(NULL, "Peer error with PSBT"
" signatures.");
goto send_msg;
}
/* Now that we've got the signed PSBT, save it */
tal_free(inflight->funding_psbt);
inflight->funding_psbt = tal_steal(inflight,
cast_const(struct wally_psbt *,
payload->psbt));
wallet_inflight_save(payload->ld->wallet, inflight);
channel_watch_funding(payload->ld, channel);
msg = towire_dualopend_send_tx_sigs(NULL, inflight->funding_psbt);
send_msg:
/* Peer's gone away, let's try reconnecting */
if (!payload->dualopend) {
channel_saved_err_broken_reconn(channel,
"dualopend daemon died"
" before signed PSBT returned");
return;
}
tal_del_destructor2(payload->dualopend,
openchannel2_psbt_remove_dualopend,
payload);
/* Send peer our signatures */
subd_send_msg(payload->dualopend, take(msg));
}
REGISTER_PLUGIN_HOOK(openchannel2,
openchannel2_hook_deserialize,
openchannel2_hook_cb,
openchannel2_hook_serialize,
struct openchannel2_payload *);
REGISTER_PLUGIN_HOOK(openchannel2_changed,
openchannel2_changed_deserialize,
openchannel2_changed_hook_cb,
openchannel2_changed_hook_serialize,
struct openchannel2_psbt_payload *);
REGISTER_PLUGIN_HOOK(openchannel2_sign,
openchannel2_signed_deserialize,
openchannel2_sign_hook_cb,
openchannel2_sign_hook_serialize,
struct openchannel2_psbt_payload *);
REGISTER_PLUGIN_HOOK(rbf_channel,
rbf_channel_hook_deserialize,
rbf_channel_hook_cb,
rbf_channel_hook_serialize,
struct rbf_channel_payload *);
static bool feerate_satisfied(struct wally_psbt *psbt,
u32 funding_feerate)
{
struct wally_tx *wtx;
size_t tx_weight;
struct amount_sat fee_paid, expected_fee;
wtx = psbt_final_tx(NULL, psbt);
tx_weight = wally_tx_weight(wtx);
tal_free(wtx);
fee_paid = psbt_compute_fee(psbt);
expected_fee = amount_tx_fee(funding_feerate, tx_weight);
return amount_sat_greater_eq(fee_paid, expected_fee);
}
static struct amount_sat calculate_reserve(struct channel_config *their_config,
struct amount_sat funding_total,
enum side opener)
{
struct amount_sat reserve, dust_limit;
/* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2
*
* The channel reserve is fixed at 1% of the total channel balance
* rounded down (sum of `funding_satoshis` from `open_channel2`
* and `accept_channel2`) or the `dust_limit_satoshis` from
* `open_channel2`, whichever is greater.
*/
reserve = amount_sat_div(funding_total, 100);
dust_limit = opener == LOCAL ?
chainparams->dust_limit :
their_config->dust_limit;
if (amount_sat_greater(dust_limit, reserve))
return dust_limit;
return reserve;
}
void channel_update_reserve(struct channel *channel,
struct channel_config *their_config,
struct amount_sat funding_total)
{
struct amount_sat reserve;
reserve = calculate_reserve(their_config,
funding_total,
channel->opener);
/* Depending on path, these are disjoint */
their_config->channel_reserve = reserve;
channel->channel_info.their_config.channel_reserve = reserve;
channel->our_config.channel_reserve = reserve;
}
/* Steals fields from uncommitted_channel: returns NULL if can't generate a
* key for this channel (shouldn't happen!). */
static struct channel_inflight *
wallet_update_channel(struct lightningd *ld,
struct channel *channel,
struct bitcoin_tx *remote_commit STEALS,
struct bitcoin_signature *remote_commit_sig,
const struct bitcoin_outpoint *funding,
struct amount_sat total_funding,
struct amount_sat our_funding,
u32 funding_feerate,
struct wally_psbt *psbt STEALS,
const u32 lease_expiry,
struct amount_sat lease_fee,
secp256k1_ecdsa_signature *lease_commit_sig STEALS,
const u32 lease_chan_max_msat,
const u16 lease_chan_max_ppt,
const u32 lease_blockheight_start,
struct amount_sat lease_amt)
{
struct amount_msat our_msat, lease_fee_msat;
struct channel_inflight *inflight;
if (!amount_sat_to_msat(&our_msat, our_funding)) {
log_broken(channel->log, "Unable to convert funds");
return NULL;
}
if (!amount_sat_to_msat(&lease_fee_msat, lease_fee)) {
log_broken(channel->log, "Unable to convert 'lease_fee'");
return NULL;
}
assert(channel->unsaved_dbid == 0);
assert(channel->dbid != 0);
channel->funding = *funding;
channel->funding_sats = total_funding;
channel->our_funds = our_funding;
channel->our_msat = our_msat;
channel->push = lease_fee_msat;
channel->msat_to_us_min = our_msat;
channel->msat_to_us_max = our_msat;
channel->lease_expiry = lease_expiry;
channel->htlc_minimum_msat = channel->channel_info.their_config.htlc_minimum;
channel->htlc_maximum_msat = htlc_max_possible_send(channel);
tal_free(channel->lease_commit_sig);
channel->lease_commit_sig = tal_steal(channel, lease_commit_sig);
channel->lease_chan_max_msat = lease_chan_max_msat;
channel->lease_chan_max_ppt = lease_chan_max_ppt;
tal_free(channel->blockheight_states);
channel->blockheight_states = new_height_states(channel,
channel->opener,
&lease_blockheight_start);
channel_set_last_tx(channel,
tal_steal(channel, remote_commit),
remote_commit_sig);
/* Update in database */
wallet_channel_save(ld->wallet, channel);
/* Add open attempt to channel's inflights */
inflight = new_inflight(channel,
&channel->funding,
funding_feerate,
channel->funding_sats,
channel->our_funds,
psbt,
channel->last_tx,
channel->last_sig,
channel->lease_expiry,
channel->lease_commit_sig,
channel->lease_chan_max_msat,
channel->lease_chan_max_ppt,
lease_blockheight_start,
channel->push,
lease_amt);
wallet_inflight_add(ld->wallet, inflight);
return inflight;
}
/* Returns NULL if can't generate a key for this channel (Shouldn't happen) */
static struct channel_inflight *
wallet_commit_channel(struct lightningd *ld,
struct channel *channel,
struct bitcoin_tx *remote_commit,
struct bitcoin_signature *remote_commit_sig,
const struct bitcoin_outpoint *funding,
struct amount_sat total_funding,
struct amount_sat our_funding,
struct channel_info *channel_info,
u32 funding_feerate,
u32 commitment_feerate,
const u8 *our_upfront_shutdown_script,
const u8 *remote_upfront_shutdown_script,
struct wally_psbt *psbt STEALS,
const struct amount_sat lease_amt,
const u32 lease_blockheight_start,
const u32 lease_expiry,
const struct amount_sat lease_fee,
secp256k1_ecdsa_signature *lease_commit_sig STEALS,
const u32 lease_chan_max_msat,
const u16 lease_chan_max_ppt,
const struct channel_type *type)
{
struct amount_msat our_msat, lease_fee_msat;
struct channel_inflight *inflight;
bool any_active = peer_any_active_channel(channel->peer, NULL);
if (!amount_sat_to_msat(&our_msat, our_funding)) {
log_broken(channel->log, "Unable to convert funds");
return NULL;
}
if (!amount_sat_to_msat(&lease_fee_msat, lease_fee)) {
log_broken(channel->log, "Unable to convert lease fee");
return NULL;
}
/* Get a key to use for closing outputs from this tx */
channel->final_key_idx = wallet_get_newindex(ld);
if (channel->final_key_idx == -1) {
log_broken(channel->log, "Can't get final key index");
return NULL;
}
/* This is a new channel_info.their_config so set its ID to 0 */
channel_info->their_config.id = 0;
/* old_remote_per_commit not valid yet, copy valid one. */
channel_info->old_remote_per_commit = channel_info->remote_per_commit;
/* Promote the unsaved_dbid to the dbid */
assert(channel->unsaved_dbid != 0);
channel->dbid = channel->unsaved_dbid;
channel->unsaved_dbid = 0;
channel->funding = *funding;
channel->funding_sats = total_funding;
channel->our_funds = our_funding;
channel->our_msat = our_msat;
channel->push = lease_fee_msat;
channel->msat_to_us_min = our_msat;
channel->msat_to_us_max = our_msat;
channel->req_confirmed_ins[LOCAL] =
ld->config.require_confirmed_inputs;
channel->last_tx = tal_steal(channel, remote_commit);
channel->last_sig = *remote_commit_sig;
channel->channel_info = *channel_info;
channel->fee_states = new_fee_states(channel,
channel->opener,
&commitment_feerate);
channel->min_possible_feerate = commitment_feerate;
channel->max_possible_feerate = commitment_feerate;
if (channel->peer->addr.itype == ADDR_INTERNAL_WIREADDR) {
channel->scb = tal(channel, struct scb_chan);
channel->scb->id = channel->dbid;
channel->scb->unused = 0;
channel->scb->addr = channel->peer->addr.u.wireaddr;
channel->scb->node_id = channel->peer->id;
channel->scb->funding = *funding;
channel->scb->cid = channel->cid;
channel->scb->funding_sats = total_funding;
} else
channel->scb = NULL;
channel->type = channel_type_dup(channel, type);
channel->scb->type = channel_type_dup(channel->scb, type);
if (our_upfront_shutdown_script)
channel->shutdown_scriptpubkey[LOCAL]
= tal_steal(channel, our_upfront_shutdown_script);
else
channel->shutdown_scriptpubkey[LOCAL]
= p2wpkh_for_keyidx(channel, channel->peer->ld,
channel->final_key_idx);
/* Can't have gotten their alias for this channel yet. */
channel->alias[REMOTE] = NULL;
/* We do generate one ourselves however. */
channel->alias[LOCAL] = tal(channel, struct short_channel_id);
randombytes_buf(channel->alias[LOCAL], sizeof(struct short_channel_id));
channel->remote_upfront_shutdown_script
= tal_steal(channel, remote_upfront_shutdown_script);
channel->state_change_cause = (channel->opener == LOCAL) ?
REASON_USER : REASON_REMOTE;
/* If we're fundee, could be a little before this
* in theory, but it's only used for timing out. */
channel->first_blocknum = get_block_height(ld->topology);
/* Update lease info for channel */
channel->blockheight_states = new_height_states(channel,
channel->opener,
&lease_blockheight_start);
channel->lease_expiry = lease_expiry;
tal_free(channel->lease_commit_sig);
channel->lease_commit_sig = tal_steal(channel, lease_commit_sig);
channel->lease_chan_max_msat = lease_chan_max_msat;
channel->lease_chan_max_ppt = lease_chan_max_ppt;
channel->htlc_minimum_msat = channel_info->their_config.htlc_minimum;
channel->htlc_maximum_msat = htlc_max_possible_send(channel);
/* Now we finally put it in the database. */
wallet_channel_insert(ld->wallet, channel);
/* Open attempt to channel's inflights */
inflight = new_inflight(channel,
&channel->funding,
funding_feerate,
channel->funding_sats,
channel->our_funds,
psbt,
channel->last_tx,
channel->last_sig,
channel->lease_expiry,
channel->lease_commit_sig,
channel->lease_chan_max_msat,
channel->lease_chan_max_ppt,
lease_blockheight_start,
channel->push,
lease_amt);
wallet_inflight_add(ld->wallet, inflight);
/* We might have disconnected and decided we didn't need to
* reconnect because no channels are active. But the subd
* just made it active! */
if (!any_active && channel->peer->connected == PEER_DISCONNECTED) {
try_reconnect(channel->peer, channel->peer,
&channel->peer->addr);
}
return inflight;
}
static void handle_peer_wants_to_close(struct subd *dualopend,
const u8 *msg)
{
u8 *scriptpubkey;
struct lightningd *ld = dualopend->ld;
struct channel *channel = dualopend->channel;
char *errmsg;
bool anysegwit = feature_negotiated(ld->our_features,
channel->peer->their_features,
OPT_SHUTDOWN_ANYSEGWIT);
bool anchors = feature_negotiated(ld->our_features,
channel->peer->their_features,
OPT_ANCHOR_OUTPUTS)
|| feature_negotiated(ld->our_features,
channel->peer->their_features,
OPT_ANCHORS_ZERO_FEE_HTLC_TX);
/* We shouldn't get this message while we're waiting to finish */
if (channel_unsaved(channel)) {
log_broken(dualopend->ld->log, "Channel in wrong state for"
" shutdown, still has uncommitted"
" channel pending.");
errmsg = "Channel not established yet, shutdown invalid";
subd_send_msg(dualopend,
take(towire_dualopend_fail(NULL, errmsg)));
return;
}
if (!fromwire_dualopend_got_shutdown(channel, msg, &scriptpubkey)) {
channel_internal_error(channel,
"Bad DUALOPEND_GOT_SHUTDOWN: %s",
tal_hex(msg, msg));
return;
}
tal_free(channel->shutdown_scriptpubkey[REMOTE]);
channel->shutdown_scriptpubkey[REMOTE] = scriptpubkey;
/* BOLT #2:
*
* A receiving node:
*...
* - if the `scriptpubkey` is not in one of the above forms:
* - SHOULD send a `warning`
*/
if (!valid_shutdown_scriptpubkey(scriptpubkey, anysegwit, !anchors)) {
u8 *warning = towire_warningfmt(NULL,
&channel->cid,
"Bad shutdown scriptpubkey %s",
tal_hex(tmpctx, scriptpubkey));
/* Get connectd to send warning, and kill subd. */
subd_send_msg(ld->connectd,
take(towire_connectd_peer_final_msg(NULL,
&channel->peer->id,
channel->peer->connectd_counter,
warning)));
channel_fail_transient(channel, "Bad shutdown scriptpubkey %s",
tal_hex(tmpctx, scriptpubkey));
return;
}
/* If we weren't already shutting down, we are now */
if (channel->state != CHANNELD_SHUTTING_DOWN)
channel_set_state(channel,
channel->state,
CHANNELD_SHUTTING_DOWN,
REASON_REMOTE,
"Peer closes channel");
/* TODO(cdecker) Selectively save updated fields to DB */
wallet_channel_save(ld->wallet, channel);
/* Now we send back our scriptpubkey to close with */
subd_send_msg(dualopend, take(towire_dualopend_send_shutdown(NULL,
channel->shutdown_scriptpubkey[LOCAL])));
}
static void handle_channel_closed(struct subd *dualopend,
const int *fds,
const u8 *msg)
{
2022-01-11 01:13:59 +00:00
struct peer_fd *peer_fd;
struct channel *channel = dualopend->channel;
if (!fromwire_dualopend_shutdown_complete(msg)) {
channel_internal_error(dualopend->channel,
"Bad DUALOPEND_SHUTDOWN_COMPLETE: %s",
tal_hex(msg, msg));
close(fds[0]);
close(fds[1]);
return;
}
2022-01-11 01:13:59 +00:00
peer_fd = new_peer_fd_arr(tmpctx, fds);
2022-01-11 01:13:59 +00:00
peer_start_closingd(channel, peer_fd);
channel_set_state(channel,
CHANNELD_SHUTTING_DOWN,
CLOSINGD_SIGEXCHANGE,
REASON_UNKNOWN,
"Start closingd");
}
static void handle_local_private_channel(struct subd *dualopend,
const u8 *msg)
{
struct amount_sat capacity;
u8 *features;
if (!fromwire_dualopend_local_private_channel(msg, msg, &capacity,
&features)) {
channel_internal_error(dualopend->channel,
"bad dualopend_local_private_channel %s",
tal_hex(msg, msg));
return;
}
tell_gossipd_local_private_channel(dualopend->ld, dualopend->channel,
capacity, features);
}
struct channel_send {
const struct wally_tx *wtx;
struct channel *channel;
const char *err_msg;
};
static void handle_tx_broadcast(struct channel_send *cs)
{
struct lightningd *ld = cs->channel->peer->ld;
const struct wally_tx *wtx = cs->wtx;
struct channel *channel = cs->channel;
struct command *cmd = channel->openchannel_signed_cmd;
struct json_stream *response;
struct bitcoin_txid txid;
struct amount_sat unused;
int num_utxos;
/* This might have spent UTXOs from our wallet */
num_utxos = wallet_extract_owned_outputs(ld->wallet,
/* FIXME: what txindex? */
wtx, false, NULL,
&unused);
if (num_utxos)
wallet_transaction_add(ld->wallet, wtx, 0, 0);
if (cmd) {
response = json_stream_success(cmd);
wally_txid(wtx, &txid);
json_add_hex_talarr(response, "tx", linearize_wtx(tmpctx, wtx));
json_add_txid(response, "txid", &txid);
json_add_channel_id(response, "channel_id", &channel->cid);
was_pending(command_success(cmd, response));
cs->channel->openchannel_signed_cmd = NULL;
}
}
static void check_utxo_block(struct bitcoind *bitcoind UNUSED,
const struct bitcoin_tx_output *txout,
void *arg)
{
struct channel_send *cs = arg;
struct command *cmd = cs->channel->openchannel_signed_cmd;
const struct wally_tx *wtx = cs->wtx;
/* note: if this tx has been included in a block *and spent*
* then this will also fail... */
if (!txout) {
if (cmd) {
was_pending(command_fail(cmd,
FUNDING_BROADCAST_FAIL,
"Error broadcasting funding "
"tx: %s. Unsent tx discarded "
"%s.",
cs->err_msg,
type_to_string(tmpctx,
struct wally_tx,
wtx)));
cs->channel->openchannel_signed_cmd = NULL;
}
log_unusual(cs->channel->log,
"Error broadcasting funding "
"tx: %s. Unsent tx discarded "
"%s.",
cs->err_msg,
type_to_string(tmpctx, struct wally_tx, wtx));
} else
handle_tx_broadcast(cs);
tal_free(cs);
}
static void sendfunding_done(struct bitcoind *bitcoind UNUSED,
bool success, const char *msg,
struct channel_send *cs)
{
struct lightningd *ld = cs->channel->peer->ld;
struct channel *channel = cs->channel;
struct command *cmd = channel->openchannel_signed_cmd;
if (!cmd && channel->opener == LOCAL)
log_unusual(channel->log,
"No outstanding command for channel %s,"
" funding sent was success? %d",
type_to_string(tmpctx, struct channel_id,
&channel->cid),
success);
if (success) {
handle_tx_broadcast(cs);
tal_free(cs);
} else {
/* If the tx was mined into a block, it's possible
* that the broadcast would fail. Verify that's not
* the case here. */
cs->err_msg = tal_strdup(cs, msg);
bitcoind_getutxout(ld->topology->bitcoind,
&channel->funding,
check_utxo_block,
cs);
}
}
static void send_funding_tx(struct channel *channel,
const struct wally_tx *wtx TAKES)
{
struct lightningd *ld = channel->peer->ld;
struct channel_send *cs;
struct bitcoin_txid txid;
cs = tal(channel, struct channel_send);
cs->channel = channel;
if (taken(wtx))
cs->wtx = tal_steal(cs, wtx);
else {
tal_wally_start();
wally_tx_clone_alloc(wtx, 0,
cast_const2(struct wally_tx **,
&cs->wtx));
tal_wally_end_onto(cs,
cast_const(struct wally_tx *,
cs->wtx), struct wally_tx);
}
wally_txid(wtx, &txid);
log_debug(channel->log,
"Broadcasting funding tx %s for channel %s. %s",
type_to_string(tmpctx, struct bitcoin_txid, &txid),
type_to_string(tmpctx, struct channel_id, &channel->cid),
type_to_string(tmpctx, struct wally_tx, cs->wtx));
bitcoind_sendrawtx(ld->topology->bitcoind,
channel->open_attempt
? (channel->open_attempt->cmd
? channel->open_attempt->cmd->id
: NULL)
: NULL,
tal_hex(tmpctx, linearize_wtx(tmpctx, cs->wtx)),
false,
sendfunding_done, cs);
}
static void handle_peer_tx_sigs_sent(struct subd *dualopend,
const int *fds,
const u8 *msg)
{
struct channel *channel = dualopend->channel;
struct channel_inflight *inflight;
const struct wally_tx *wtx;
if (!fromwire_dualopend_tx_sigs_sent(msg)) {
channel_internal_error(channel,
"Bad WIRE_DUALOPEND_TX_SIGS_SENT: %s",
tal_hex(tmpctx, msg));
return;
}
inflight = channel_current_inflight(channel);
if (!inflight) {
channel_internal_error(channel,
"No inflight found for channel %s",
type_to_string(tmpctx, struct channel,
channel));
return;
}
/* Once we've sent our sigs to the peer, we're fine
* to broadcast the transaction, even if they haven't
* sent us their tx-sigs yet. They're not allowed to
* send us funding-locked until their tx-sigs has been
* received, but that's no reason not to broadcast.
* Note this only happens if we're the only input-er */
if (psbt_finalize(inflight->funding_psbt) &&
!inflight->tx_broadcast) {
inflight->tx_broadcast = true;
wtx = psbt_final_tx(NULL, inflight->funding_psbt);
if (!wtx) {
channel_internal_error(channel,
"Unable to extract final tx"
" from PSBT %s",
type_to_string(tmpctx,
struct wally_psbt,
inflight->funding_psbt));
return;
}
/* Saves the now finalized version of the psbt */
wallet_inflight_save(dualopend->ld->wallet, inflight);
send_funding_tx(channel, take(wtx));
/* Must be in an "init" state */
assert(channel->state == DUALOPEND_OPEN_INIT
|| channel->state == DUALOPEND_AWAITING_LOCKIN);
channel_set_state(channel, channel->state,
DUALOPEND_AWAITING_LOCKIN,
REASON_UNKNOWN,
"Sigs exchanged, waiting for lock-in");
/* Mimic the old behavior, notify a channel has been opened,
* for the accepter side */
if (channel->opener == REMOTE)
/* Tell plugins about the success */
notify_channel_opened(dualopend->ld,
&channel->peer->id,
&channel->funding_sats,
&channel->funding.txid,
channel->remote_channel_ready);
/* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2
* The receiving node: ...
* - MUST fail the channel if:
* - the `witness_stack` weight lowers the
* effective `feerate` below the agreed upon
* transaction `feerate`
*/
if (!feerate_satisfied(inflight->funding_psbt,
inflight->funding->feerate)) {
char *errmsg = tal_fmt(tmpctx,
"Witnesses lower effective"
" feerate below agreed upon rate"
" of %dperkw. Failing channel."
" Offending PSBT: %s",
inflight->funding->feerate,
type_to_string(tmpctx,
struct wally_psbt,
inflight->funding_psbt));
/* Notify the peer we're failing */
subd_send_msg(dualopend,
take(towire_dualopend_fail(NULL, errmsg)));
}
}
}
static void handle_dry_run_finished(struct subd *dualopend, const u8 *msg)
{
struct json_stream *response;
struct channel_id c_id;
struct channel *channel = dualopend->channel;
struct command *cmd;
struct lease_rates *rates;
struct amount_sat their_funding, our_funding;
bool requires_confirms;
assert(channel->open_attempt);
cmd = channel->open_attempt->cmd;
channel->open_attempt->cmd = NULL;
if (!fromwire_dualopend_dry_run(msg, msg, &c_id,
&our_funding,
&their_funding,
&requires_confirms,
&rates)) {
channel_internal_error(channel,
"Bad WIRE_DUALOPEND_DRY_RUN_FINISHED: %s",
tal_hex(msg, msg));
return;
}
/* Free up this open attempt */
channel->open_attempt = tal_free(channel->open_attempt);
response = json_stream_success(cmd);
json_add_amount_sat_msat(response, "our_funding_msat", our_funding);
json_add_amount_sat_msat(response, "their_funding_msat", their_funding);
json_add_bool(response, "requires_confirmed_inputs", requires_confirms);
if (rates) {
json_add_lease_rates(response, rates);
/* As a convenience, add a hexstring version of this data */
json_add_string(response, "compact_lease",
lease_rates_tohex(tmpctx, rates));
}
was_pending(command_success(cmd, response));
}
2020-12-10 20:25:54 +00:00
static void handle_peer_locked(struct subd *dualopend, const u8 *msg)
{
struct pubkey remote_per_commit;
struct channel *channel = dualopend->channel;
2020-12-10 20:25:54 +00:00
if (!fromwire_dualopend_peer_locked(msg, &remote_per_commit)) {
2020-12-10 20:25:54 +00:00
channel_internal_error(channel,
"Bad WIRE_DUALOPEND_PEER_LOCKED: %s",
2020-12-10 20:25:54 +00:00
tal_hex(msg, msg));
return;
}
2020-12-10 20:25:54 +00:00
/* Updates channel with the next per-commit point etc, calls
* channel_internal_error on failure */
if (!channel_on_channel_ready(channel, &remote_per_commit))
return;
2020-12-10 20:25:54 +00:00
/* Remember that we got the lock-in */
wallet_channel_save(dualopend->ld->wallet, channel);
}
static void handle_channel_locked(struct subd *dualopend,
const int *fds,
const u8 *msg)
{
struct channel *channel = dualopend->channel;
2022-01-11 01:13:59 +00:00
struct peer_fd *peer_fd;
if (!fromwire_dualopend_channel_locked(msg)) {
channel_internal_error(channel,
"Bad WIRE_DUALOPEND_CHANNEL_LOCKED: %s",
tal_hex(msg, msg));
return;
}
2022-01-11 01:13:59 +00:00
peer_fd = new_peer_fd_arr(tmpctx, fds);
assert(channel->scid);
assert(channel->remote_channel_ready);
/* This can happen if we missed their sigs, for some reason */
if (channel->state != DUALOPEND_AWAITING_LOCKIN)
log_debug(channel->log, "Lockin complete, but state %s",
channel_state_name(channel));
channel_set_state(channel,
channel->state,
CHANNELD_NORMAL,
REASON_UNKNOWN,
"Lockin complete");
channel_record_open(channel,
short_channel_id_blocknum(channel->scid),
true);
/* Empty out the inflights */
wallet_channel_clear_inflights(dualopend->ld->wallet, channel);
/* FIXME: LND sigs/update_fee msgs? */
2022-01-11 01:13:59 +00:00
peer_start_channeld(channel, peer_fd, NULL, false, NULL);
return;
}
void dualopen_tell_depth(struct subd *dualopend,
struct channel *channel,
const struct bitcoin_txid *txid,
u32 depth)
{
const u8 *msg;
u32 to_go;
if (depth < channel->minimum_depth) {
to_go = channel->minimum_depth - depth;
} else
to_go = 0;
/* Are we there yet? */
if (to_go == 0) {
assert(channel->scid);
assert(bitcoin_txid_eq(&channel->funding.txid, txid));
channel_set_billboard(channel, false,
tal_fmt(tmpctx, "Funding depth reached"
" %d confirmations, alerting peer"
" we're locked-in.",
to_go));
msg = towire_dualopend_depth_reached(NULL, depth);
subd_send_msg(dualopend, take(msg));
} else
channel_set_billboard(channel, false,
tal_fmt(tmpctx, "Funding needs %d more"
" confirmations to be ready.",
to_go));
}
static void rbf_got_offer(struct subd *dualopend, const u8 *msg)
{
/* We expect the channel to still exist?! */
struct channel *channel;
struct rbf_channel_payload *payload;
channel = dualopend->channel;
payload = tal(dualopend, struct rbf_channel_payload);
payload->dualopend = dualopend;
payload->channel = channel;
if (!fromwire_dualopend_got_rbf_offer(payload, msg,
&payload->channel_id,
&payload->their_last_funding,
&payload->their_proposed_funding,
&payload->our_last_funding,
&payload->funding_feerate_per_kw,
&payload->locktime,
&payload->requested_lease_amt)) {
channel_internal_error(channel,
"Bad WIRE_DUALOPEND_GOT_RBF_OFFER: %s",
tal_hex(msg, msg));
return;
}
/* There's currently another attempt in progress? */
if (channel->open_attempt) {
log_debug(channel->log,
"RBF attempted while previous attempt"
" is still in progress");
subd_send_msg(dualopend,
take(towire_dualopend_fail(NULL,
"Error. Already negotiation"
" in progress")));
return;
}
assert(channel_id_eq(&channel->cid, &payload->channel_id));
/* Fill in general channel info from channel */
payload->peer_id = channel->peer->id;
payload->feerate_our_max = feerate_max(dualopend->ld, NULL);
payload->feerate_our_min = feerate_min(dualopend->ld, NULL);
payload->req_confirmed_ins_remote =
channel->req_confirmed_ins[REMOTE];
payload->psbt = NULL;
/* No error message known (yet) */
payload->err_msg = NULL;
if (feature_negotiated(dualopend->ld->our_features,
channel->peer->their_features,
OPT_LARGE_CHANNELS))
payload->channel_max = chainparams->max_supply;
else
payload->channel_max = chainparams->max_funding;
tal_add_destructor2(dualopend, rbf_channel_remove_dualopend, payload);
plugin_hook_call_rbf_channel(dualopend->ld, NULL, payload);
}
static void accepter_got_offer(struct subd *dualopend,
struct channel *channel,
const u8 *msg)
{
struct openchannel2_payload *payload;
if (channel->open_attempt) {
subd_send_msg(dualopend,
take(towire_dualopend_fail(NULL,
"Already initiated channel open")));
return;
}
payload = tal(dualopend, struct openchannel2_payload);
payload->dualopend = dualopend;
payload->channel = channel;
payload->psbt = NULL;
payload->accepter_funding = AMOUNT_SAT(0);
payload->our_shutdown_scriptpubkey = NULL;
payload->peer_id = channel->peer->id;
payload->rates = NULL;
payload->err_msg = NULL;
if (!fromwire_dualopend_got_offer(payload, msg,
&payload->channel_id,
&payload->their_funding,
&payload->dust_limit_satoshis,
&payload->max_htlc_value_in_flight_msat,
&payload->htlc_minimum_msat,
&payload->funding_feerate_per_kw,
&payload->commitment_feerate_per_kw,
&payload->to_self_delay,
&payload->max_accepted_htlcs,
&payload->channel_flags,
&payload->locktime,
&payload->shutdown_scriptpubkey,
&payload->requested_lease_amt,
&payload->lease_blockheight_start,
&payload->req_confirmed_ins_remote)) {
channel_internal_error(channel, "Bad DUALOPEND_GOT_OFFER: %s",
tal_hex(tmpctx, msg));
return;
}
/* As a convenience to the plugin, we provide our current known
* min + max feerates. Ideally, the plugin will fail to
* contribute funds if the peer's feerate range is outside of
* this acceptable range, but we delegate that decision to
* the plugin */
payload->feerate_our_min = feerate_min(dualopend->ld, NULL);
payload->feerate_our_max = feerate_max(dualopend->ld, NULL);
payload->node_blockheight = get_block_height(dualopend->ld->topology);
if (feature_negotiated(dualopend->ld->our_features,
channel->peer->their_features,
OPT_LARGE_CHANNELS))
payload->channel_max = chainparams->max_supply;
else
payload->channel_max = chainparams->max_funding;
tal_add_destructor2(dualopend, openchannel2_remove_dualopend, payload);
plugin_hook_call_openchannel2(dualopend->ld, NULL, payload);
}
static void handle_peer_tx_sigs_msg(struct subd *dualopend,
const u8 *msg)
{
struct wally_psbt *psbt;
const struct wally_tx *wtx;
struct lightningd *ld = dualopend->ld;
struct channel *channel = dualopend->channel;
struct channel_inflight *inflight;
if (!fromwire_dualopend_funding_sigs(tmpctx, msg, &psbt)) {
channel_internal_error(channel,
"Bad DUALOPEND_FUNDING_SIGS: %s",
tal_hex(tmpctx, msg));
return;
}
inflight = channel_current_inflight(channel);
if (!inflight) {
channel_internal_error(channel,
"No inflight found for channel %s",
type_to_string(tmpctx, struct channel,
channel));
return;
}
/* Save that we've gotten their sigs. Sometimes
* the peer doesn't send any sigs (no inputs), otherwise
* we could just check the PSBT was finalized */
inflight->remote_tx_sigs = true;
tal_wally_start();
if (wally_psbt_combine(inflight->funding_psbt, psbt) != WALLY_OK) {
channel_internal_error(channel,
"Unable to combine PSBTs: %s, %s",
type_to_string(tmpctx,
struct wally_psbt,
inflight->funding_psbt),
type_to_string(tmpctx,
struct wally_psbt,
psbt));
tal_wally_end(inflight->funding_psbt);
return;
}
tal_wally_end(inflight->funding_psbt);
wallet_inflight_save(ld->wallet, inflight);
/* It's possible we haven't sent them our (empty) tx-sigs yet,
* but we should be sending it soon... */
if (psbt_finalize(cast_const(struct wally_psbt *,
inflight->funding_psbt))
&& !inflight->tx_broadcast) {
inflight->tx_broadcast = true;
/* Saves the now finalized version of the psbt */
wallet_inflight_save(ld->wallet, inflight);
wtx = psbt_final_tx(NULL, inflight->funding_psbt);
if (!wtx) {
channel_internal_error(channel,
"Unable to extract final tx"
" from PSBT %s",
type_to_string(tmpctx,
struct wally_psbt,
inflight->funding_psbt));
return;
}
send_funding_tx(channel, take(wtx));
2021-01-25 18:04:02 +00:00
assert(channel->state == DUALOPEND_OPEN_INIT
/* We might be reconnecting */
|| channel->state == DUALOPEND_AWAITING_LOCKIN);
channel_set_state(channel, channel->state,
DUALOPEND_AWAITING_LOCKIN,
REASON_UNKNOWN,
"Sigs exchanged, waiting for lock-in");
/* Mimic the old behavior, notify a channel has been opened,
* for the accepter side */
if (channel->opener == REMOTE)
/* Tell plugins about the success */
notify_channel_opened(dualopend->ld,
&channel->peer->id,
&channel->funding_sats,
&channel->funding.txid,
channel->remote_channel_ready);
/* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2
* The receiving node: ...
* - MUST fail the channel if:
* - the `witness_stack` weight lowers the
* effective `feerate` below the agreed upon
* transaction `feerate`
*/
if (!feerate_satisfied(inflight->funding_psbt,
inflight->funding->feerate)) {
char *errmsg = tal_fmt(tmpctx,
"Witnesses lower effective"
" feerate below agreed upon rate"
" of %dperkw. Failing channel."
" Offending PSBT: %s",
inflight->funding->feerate,
type_to_string(tmpctx,
struct wally_psbt,
inflight->funding_psbt));
/* Notify the peer we're failing */
subd_send_msg(dualopend,
take(towire_dualopend_fail(NULL, errmsg)));
}
}
/* Send notification with peer's signed PSBT */
notify_openchannel_peer_sigs(ld, &channel->cid,
inflight->funding_psbt);
}
static bool verify_option_will_fund_signature(struct peer *peer,
struct pubkey *funding_pubkey,
u32 lease_expiry,
u32 chan_fee_msat,
u16 chan_fee_ppt,
const secp256k1_ecdsa_signature *sig)
{
struct pubkey their_pubkey;
struct sha256 sha;
int ret;
lease_rates_get_commitment(funding_pubkey, lease_expiry,
chan_fee_msat, chan_fee_ppt,
&sha);
if (!pubkey_from_node_id(&their_pubkey, &peer->id)) {
log_broken(peer->ld->log,
"Unable to extract pubkey from peer's node id %s",
type_to_string(tmpctx, struct node_id, &peer->id));
return false;
}
ret = secp256k1_ecdsa_verify(secp256k1_ctx, sig, sha.u.u8,
&their_pubkey.pubkey);
return ret == 1;
}
static void handle_validate_lease(struct subd *dualopend,
const u8 *msg)
{
const secp256k1_ecdsa_signature sig;
u16 chan_fee_max_ppt;
u32 chan_fee_max_base_msat, lease_expiry;
struct pubkey their_pubkey;
struct channel *chan;
char *err_msg;
if (!fromwire_dualopend_validate_lease(msg,
cast_const(secp256k1_ecdsa_signature *, &sig),
&lease_expiry,
&chan_fee_max_base_msat,
&chan_fee_max_ppt,
&their_pubkey)) {
channel_internal_error(dualopend->channel,
"Bad DUALOPEND_VALIDATE_LEASE: %s",
tal_hex(tmpctx, msg));
return;
}
assert(dualopend->channel);
chan = dualopend->channel;
if (!verify_option_will_fund_signature(chan->peer, &their_pubkey,
lease_expiry,
chan_fee_max_base_msat,
chan_fee_max_ppt,
&sig))
err_msg = "Unable to verify sig";
else
err_msg = NULL;
subd_send_msg(dualopend,
take(towire_dualopend_validate_lease_reply(NULL, err_msg)));
}
static void handle_validate_rbf(struct subd *dualopend,
const u8 *msg)
{
struct wally_psbt *candidate_psbt;
struct channel_inflight *inflight;
struct channel *channel = dualopend->channel;
bool *inputs_present;
struct amount_sat candidate_fee, last_fee;
if (!fromwire_dualopend_rbf_validate(tmpctx, msg,
&candidate_psbt)) {
channel_internal_error(channel,
"Bad DUALOPEND_RBF_VALIDATE: %s",
tal_hex(tmpctx, msg));
return;
}
inputs_present = tal_arr(tmpctx, bool, candidate_psbt->num_inputs);
memset(inputs_present, true, tal_bytelen(inputs_present));
/* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2:
* The receiving node: ...
* - MUST fail the negotiation if: ...
* - the transaction does not share a common input with
* all previous funding transactions
*/
list_for_each(&channel->inflights, inflight, list) {
/* Remove every non-matching input from set */
for (size_t i = 0; i < candidate_psbt->num_inputs; i++) {
const struct wally_psbt_input *input =
&candidate_psbt->inputs[i];
struct bitcoin_outpoint outpoint;
wally_psbt_input_get_outpoint(input, &outpoint);
if (!psbt_has_input(inflight->funding_psbt,
&outpoint))
inputs_present[i] = false;
}
}
/* Are there any inputs that were present on all txs? */
if (memeqzero(inputs_present, tal_bytelen(inputs_present))) {
char *errmsg;
inflight = list_tail(&channel->inflights,
struct channel_inflight,
list);
assert(inflight);
errmsg = tal_fmt(tmpctx, "No overlapping input"
" present. New: %s, last: %s",
type_to_string(tmpctx,
struct wally_psbt,
candidate_psbt),
type_to_string(tmpctx,
struct wally_psbt,
inflight->funding_psbt));
msg = towire_dualopend_fail(NULL, errmsg);
goto send_msg;
}
candidate_fee = psbt_compute_fee(candidate_psbt);
inflight = list_tail(&channel->inflights,
struct channel_inflight,
list);
assert(inflight);
last_fee = psbt_compute_fee(inflight->funding_psbt);
/* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2:
* The receiving node: ...
* - if is an RBF attempt:
* - MUST fail the negotiation if:
* - the transaction's total fees is less than the last
* successfully negotiated transaction's fees
*/
if (!amount_sat_greater(candidate_fee, last_fee)) {
char *errmsg = tal_fmt(tmpctx, "Proposed funding tx fee (%s)"
" less than/equal to last (%s)",
type_to_string(tmpctx,
struct amount_sat,
&candidate_fee),
type_to_string(tmpctx,
struct amount_sat,
&last_fee));
msg = towire_dualopend_fail(NULL, errmsg);
goto send_msg;
}
msg = towire_dualopend_rbf_valid(NULL);
send_msg:
subd_send_msg(channel->owner, take(msg));
}
static struct command_result *
json_openchannel_abort(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct channel_id *cid;
struct channel *channel;
u8 *msg;
if (!param(cmd, buffer, params,
p_req("channel_id", param_channel_id, &cid),
NULL))
return command_param_failed();
channel = channel_by_cid(cmd->ld, cid);
if (!channel)
return command_fail(cmd, FUNDING_UNKNOWN_CHANNEL,
"Unknown channel %s",
type_to_string(tmpctx, struct channel_id,
cid));
if (!channel->owner)
return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED,
"Peer not connected");
if (!channel->open_attempt) {
if (list_empty(&channel->inflights))
return command_fail(cmd, FUNDING_STATE_INVALID,
"Channel open not in progress");
return command_fail(cmd, FUNDING_STATE_INVALID,
"Sigs already exchanged, can't cancel");
}
if (channel->open_attempt->cmd)
return command_fail(cmd, FUNDING_STATE_INVALID,
"Another openchannel command"
" is in progress");
if (channel->openchannel_signed_cmd)
return command_fail(cmd, FUNDING_STATE_INVALID,
"Already sent sigs, waiting for peer's");
/* Mark it as aborted so when we clean-up, we send the
* correct response */
channel->open_attempt->aborted = true;
channel->open_attempt->cmd = cmd;
/* Tell dualopend to fail this channel */
msg = towire_dualopend_fail(NULL, "Abort requested");
subd_send_msg(channel->owner, take(msg));
return command_still_pending(cmd);
}
static struct command_result *
json_openchannel_bump(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct channel_id *cid;
struct channel *channel;
struct amount_sat *amount, psbt_val;
struct wally_psbt *psbt;
u32 last_feerate_perkw, next_feerate_min, *feerate_per_kw_funding;
struct open_attempt *oa;
if (!param(cmd, buffer, params,
p_req("channel_id", param_channel_id, &cid),
p_req("amount", param_sat, &amount),
p_req("initialpsbt", param_psbt, &psbt),
p_opt("funding_feerate", param_feerate,
&feerate_per_kw_funding),
NULL))
return command_param_failed();
psbt_val = AMOUNT_SAT(0);
for (size_t i = 0; i < psbt->num_inputs; i++) {
struct amount_sat in_amt = psbt_input_get_amount(psbt, i);
if (!amount_sat_add(&psbt_val, psbt_val, in_amt))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Overflow in adding PSBT input"
" values. %s",
type_to_string(tmpctx,
struct wally_psbt,
psbt));
}
/* If they don't pass in at least enough in the PSBT to cover
* their amount, nope */
if (!amount_sat_greater(psbt_val, *amount))
return command_fail(cmd, FUND_CANNOT_AFFORD,
"Provided PSBT cannot afford funding of "
"amount %s. %s",
type_to_string(tmpctx,
struct amount_sat,
amount),
type_to_string(tmpctx,
struct wally_psbt,
psbt));
if (!topology_synced(cmd->ld->topology)) {
return command_fail(cmd, FUNDING_STILL_SYNCING_BITCOIN,
"Still syncing with bitcoin network");
}
/* Are we in a state where we can attempt an RBF? */
channel = channel_by_cid(cmd->ld, cid);
if (!channel)
return command_fail(cmd, FUNDING_UNKNOWN_CHANNEL,
"Unknown channel %s",
type_to_string(tmpctx, struct channel_id,
cid));
last_feerate_perkw = channel_last_funding_feerate(channel);
next_feerate_min = last_feerate_perkw * 65 / 64;
assert(next_feerate_min > last_feerate_perkw);
if (!feerate_per_kw_funding) {
feerate_per_kw_funding = tal(cmd, u32);
*feerate_per_kw_funding = next_feerate_min;
} else if (*feerate_per_kw_funding < next_feerate_min)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Next feerate must be at least 1/64th"
" greater than the last. Min req %u,"
" you proposed %u",
next_feerate_min,
*feerate_per_kw_funding);
/* BOLT #2:
* - if both nodes advertised `option_support_large_channel`:
* - MAY set `funding_satoshis` greater than or equal to 2^24 satoshi.
* - otherwise:
* - MUST set `funding_satoshis` to less than 2^24 satoshi.
*/
if (!feature_negotiated(cmd->ld->our_features,
channel->peer->their_features,
OPT_LARGE_CHANNELS)
&& amount_sat_greater(*amount, chainparams->max_funding))
return command_fail(cmd, FUND_MAX_EXCEEDED,
"Amount exceeded %s",
type_to_string(tmpctx, struct amount_sat,
&chainparams->max_funding));
/* It's possible that the last open failed/was aborted.
* So now we restart the attempt! */
if (!channel->owner) {
int fds[2];
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) {
log_broken(channel->log,
"Failed to create socketpair: %s",
strerror(errno));
return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED,
"Unable to create socket: %s",
strerror(errno));
}
if (!peer_restart_dualopend(channel->peer,
new_peer_fd(tmpctx, fds[0]),
channel)) {
close(fds[1]);
return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED,
"Peer not connected.");
}
subd_send_msg(cmd->ld->connectd,
take(towire_connectd_peer_connect_subd(NULL,
&channel->peer->id,
channel->peer->connectd_counter,
&channel->cid)));
subd_send_fd(cmd->ld->connectd, fds[1]);
}
if (channel->open_attempt)
return command_fail(cmd, FUNDING_STATE_INVALID,
"Commitments for this channel not "
"secured, see `openchannel_update`");
if (channel->state != DUALOPEND_AWAITING_LOCKIN)
return command_fail(cmd, FUNDING_STATE_INVALID,
"Channel not eligible to init RBF."
" Current state %s, expected state %s",
channel_state_name(channel),
channel_state_str(DUALOPEND_AWAITING_LOCKIN));
if (channel->opener != LOCAL)
return command_fail(cmd, FUNDING_STATE_INVALID,
"Only the channel opener can initiate an"
" RBF attempt");
/* Ok, we're kosher to start */
channel->open_attempt = oa = new_channel_open_attempt(channel);
oa->funding = *amount;
oa->cmd = cmd;
oa->our_upfront_shutdown_script
= channel->shutdown_scriptpubkey[LOCAL];
/* Add serials to any input that's missing them */
psbt_add_serials(psbt, TX_INITIATOR);
/* We require the PSBT to meet certain criteria such as
* extra, proprietary fields (`serial_id`s) or
* to have a `redeemscripts` iff the inputs are P2SH.
*
* Since this is externally provided, we confirm that
* they've done the right thing / haven't lost any required info.
*/
if (!psbt_has_required_fields(psbt))
return command_fail(cmd, FUNDING_PSBT_INVALID,
"PSBT is missing required fields %s",
type_to_string(tmpctx, struct wally_psbt,
psbt));
subd_send_msg(channel->owner,
take(towire_dualopend_rbf_init(NULL, *amount,
*feerate_per_kw_funding,
psbt)));
return command_still_pending(cmd);
}
2020-11-24 00:36:22 +00:00
static struct command_result *
json_openchannel_signed(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct wally_psbt *psbt;
struct channel_id *cid;
struct channel *channel;
struct bitcoin_txid txid;
struct channel_inflight *inflight;
if (!param(cmd, buffer, params,
p_req("channel_id", param_channel_id, &cid),
p_req("signed_psbt", param_psbt, &psbt),
NULL))
return command_param_failed();
channel = channel_by_cid(cmd->ld, cid);
if (!channel)
return command_fail(cmd, FUNDING_UNKNOWN_CHANNEL,
"Unknown channel");
if (!channel->owner)
return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED,
"Peer not connected");
if (channel->open_attempt)
return command_fail(cmd, FUNDING_STATE_INVALID,
"Commitments for this channel not "
"yet secured, see `openchannel_update`");
if (list_empty(&channel->inflights))
return command_fail(cmd, FUNDING_STATE_INVALID,
"Channel open not initialized yet.");
if (channel->openchannel_signed_cmd)
return command_fail(cmd, FUNDING_STATE_INVALID,
"Already sent sigs, waiting for peer's");
/* Verify that the psbt's txid matches that of the
* funding txid for this channel */
psbt_txid(NULL, psbt, &txid, NULL);
if (!bitcoin_txid_eq(&txid, &channel->funding.txid))
return command_fail(cmd, FUNDING_PSBT_INVALID,
"Txid for passed in PSBT does not match"
" funding txid for channel. Expected %s, "
"received %s",
type_to_string(tmpctx, struct bitcoin_txid,
&channel->funding.txid),
type_to_string(tmpctx, struct bitcoin_txid,
&txid));
inflight = list_tail(&channel->inflights,
struct channel_inflight,
list);
if (!inflight)
return command_fail(cmd, LIGHTNINGD,
"Open attempt for channel not found");
if (!bitcoin_txid_eq(&txid, &inflight->funding->outpoint.txid))
return command_fail(cmd, LIGHTNINGD,
"Current inflight transaction is %s,"
" not %s",
type_to_string(tmpctx, struct bitcoin_txid,
&txid),
type_to_string(tmpctx, struct bitcoin_txid,
&inflight->funding
->outpoint.txid));
if (inflight->funding_psbt && psbt_is_finalized(inflight->funding_psbt))
return command_fail(cmd, FUNDING_STATE_INVALID,
"Already have a finalized PSBT for "
"this channel");
/* Go ahead and try to finalize things, or what we can */
psbt_finalize(psbt);
/* Check that all of *our* outputs are finalized */
if (!psbt_side_finalized(psbt, channel->opener == LOCAL ?
TX_INITIATOR : TX_ACCEPTER))
return command_fail(cmd, FUNDING_PSBT_INVALID,
"Local PSBT input(s) not finalized");
/* Now that we've got the signed PSBT, save it */
tal_wally_start();
if (wally_psbt_combine(cast_const(struct wally_psbt *,
inflight->funding_psbt),
psbt) != WALLY_OK) {
tal_wally_end(tal_free(inflight->funding_psbt));
return command_fail(cmd, FUNDING_PSBT_INVALID,
"Failed adding sigs");
}
/* Make memleak happy, (otherwise cleaned up with `cmd`) */
tal_free(psbt);
tal_wally_end_onto(inflight, inflight->funding_psbt, struct wally_psbt);
/* Update the PSBT on disk */
wallet_inflight_save(cmd->ld->wallet, inflight);
/* Uses the channel->funding_txid, which we verified above */
channel_watch_funding(cmd->ld, channel);
/* Send our tx_sigs to the peer */
subd_send_msg(channel->owner,
take(towire_dualopend_send_tx_sigs(NULL,
inflight->funding_psbt)));
channel->openchannel_signed_cmd = tal_steal(channel, cmd);
return command_still_pending(cmd);
}
struct psbt_validator {
struct command *cmd;
struct channel *channel;
struct wally_psbt *psbt;
enum tx_role role_to_validate;
size_t next_index;
/* on success */
void (*success)(struct psbt_validator *pv);
/* on invalid psbt input */
void (*invalid_input)(struct psbt_validator *pv, const char *err_msg);
};
static void validate_input_unspent(struct bitcoind *bitcoind,
const struct bitcoin_tx_output *txout,
void *arg)
{
struct psbt_validator *pv = arg;
char *err;
/* First time thru bitcoind will be NULL, otherwise is response */
if (bitcoind && !txout) {
struct bitcoin_outpoint outpoint;
assert(pv->next_index > 0);
wally_psbt_input_get_outpoint(&pv->psbt->inputs[pv->next_index - 1],
&outpoint);
err = tal_fmt(pv, "Requested only confirmed"
" inputs for this open."
" Input %s is not confirmed.",
type_to_string(tmpctx,
struct bitcoin_outpoint,
&outpoint));
pv->invalid_input(pv, err);
return;
}
for (size_t i = pv->next_index; i < pv->psbt->num_inputs; i++) {
struct bitcoin_outpoint outpoint;
u64 serial;
if (!psbt_get_serial_id(&pv->psbt->inputs[i].unknowns, &serial)) {
err = tal_fmt(pv, "PSBT input at index %zu"
" missing serial id", i);
pv->invalid_input(pv, err);
return;
}
/* Ignore any input that's not what we're looking for */
if (serial % 2 != pv->role_to_validate)
continue;
wally_psbt_input_get_outpoint(&pv->psbt->inputs[i],
&outpoint);
pv->next_index = i + 1;
/* Confirm input is in a block */
bitcoind_getutxout(pv->channel->owner->ld->topology->bitcoind,
&outpoint,
validate_input_unspent,
pv);
return;
}
pv->success(pv);
}
static void openchannel_update_valid_psbt(struct psbt_validator *pv)
{
u8 *msg;
assert(pv->cmd);
pv->channel->open_attempt->cmd = pv->cmd;
msg = towire_dualopend_psbt_updated(NULL, pv->psbt);
subd_send_msg(pv->channel->owner, take(msg));
}
static void openchannel_invalid_psbt(struct psbt_validator *pv, const char *err_msg)
{
assert(pv->cmd);
was_pending(command_fail(pv->cmd,
FUNDING_PSBT_INVALID,
"%s", err_msg));
}
static struct command_result *json_openchannel_update(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct wally_psbt *psbt;
struct channel_id *cid;
struct channel *channel;
struct psbt_validator *pv;
struct command_result *ret;
if (!param(cmd, buffer, params,
p_req("channel_id", param_channel_id, &cid),
p_req("psbt", param_psbt, &psbt),
NULL))
return command_param_failed();
channel = channel_by_cid(cmd->ld, cid);
if (!channel)
return command_fail(cmd, FUNDING_UNKNOWN_CHANNEL,
"Unknown channel %s",
type_to_string(tmpctx, struct channel_id,
cid));
if (!channel->owner)
return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED,
"Peer not connected");
if (!channel->open_attempt)
return command_fail(cmd, FUNDING_STATE_INVALID,
"Channel open not in progress");
if (channel->open_attempt->cmd)
return command_fail(cmd, FUNDING_STATE_INVALID,
"Another openchannel command"
" is in progress");
/* Add serials to PSBT */
psbt_add_serials(psbt, TX_INITIATOR);
/* We require the PSBT to meet certain criteria such as
* extra, proprietary fields (`serial_id`s) or
* to have a `redeemscripts` iff the inputs are P2SH.
*
* Since this is externally provided, we confirm that
* they've done the right thing / haven't lost any required info.
*/
if (!psbt_has_required_fields(psbt))
return command_fail(cmd, FUNDING_PSBT_INVALID,
"PSBT is missing required fields %s",
type_to_string(tmpctx, struct wally_psbt,
psbt));
/* Set up the psbt-validator, we only validate in the
* case of requiring confirmations */
pv = tal(cmd, struct psbt_validator);
pv->cmd = cmd;
pv->channel = channel;
pv->next_index = 0;
pv->psbt = psbt;
pv->role_to_validate = TX_INITIATOR;
pv->success = openchannel_update_valid_psbt;
pv->invalid_input = openchannel_invalid_psbt;
if (channel->req_confirmed_ins[REMOTE]) {
/* We might fail/terminate in validate's first call,
* which expects us to be at "command still pending" */
ret = command_still_pending(cmd);
validate_input_unspent(NULL, NULL, pv);
return ret;
}
/* Jump straight to the end here! */
openchannel_update_valid_psbt(pv);
return command_still_pending(cmd);
}
static struct command_result *init_set_feerate(struct command *cmd,
u32 **feerate_per_kw,
u32 **feerate_per_kw_funding)
{
if (!*feerate_per_kw_funding) {
*feerate_per_kw_funding = tal(cmd, u32);
**feerate_per_kw_funding = opening_feerate(cmd->ld->topology);
if (!**feerate_per_kw_funding)
return command_fail(cmd, LIGHTNINGD,
"`funding_feerate` not specified and fee "
"estimation failed");
}
if (!*feerate_per_kw) {
*feerate_per_kw = tal(cmd, u32);
**feerate_per_kw = **feerate_per_kw_funding;
}
return NULL;
}
static struct command_result *json_openchannel_init(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct node_id *id;
struct peer *peer;
struct channel *channel;
bool *announce_channel;
u32 *feerate_per_kw_funding;
u32 *feerate_per_kw;
struct amount_sat *amount, psbt_val, *request_amt;
struct wally_psbt *psbt;
const u8 *our_upfront_shutdown_script;
2021-11-04 17:48:43 +00:00
u32 *our_upfront_shutdown_script_wallet_index;
struct open_attempt *oa;
struct lease_rates *rates;
struct command_result *res;
int fds[2];
if (!param(cmd, buffer, params,
p_req("id", param_node_id, &id),
p_req("amount", param_sat, &amount),
p_req("initialpsbt", param_psbt, &psbt),
p_opt("commitment_feerate", param_feerate, &feerate_per_kw),
p_opt("funding_feerate", param_feerate, &feerate_per_kw_funding),
p_opt_def("announce", param_bool, &announce_channel, true),
p_opt("close_to", param_bitcoin_address, &our_upfront_shutdown_script),
p_opt_def("request_amt", param_sat, &request_amt, AMOUNT_SAT(0)),
p_opt("compact_lease", param_lease_hex, &rates),
NULL))
return command_param_failed();
/* We only deal in v2 */
if (!psbt_set_version(psbt, 2)) {
return command_fail(cmd, LIGHTNINGD, "Could not set PSBT version.");
}
/* Gotta expect some rates ! */
if (!amount_sat_zero(*request_amt) && !rates)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Must pass in 'compact_lease' if requesting"
" funds from peer");
psbt_val = AMOUNT_SAT(0);
for (size_t i = 0; i < psbt->num_inputs; i++) {
struct amount_sat in_amt = psbt_input_get_amount(psbt, i);
if (!amount_sat_add(&psbt_val, psbt_val, in_amt))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
2021-01-20 01:39:35 +00:00
"Overflow in adding PSBT input"
" values. %s",
type_to_string(tmpctx,
struct wally_psbt,
psbt));
}
/* If they don't pass in at least enough in the PSBT to cover
* their amount, nope */
if (!amount_sat_greater(psbt_val, *amount))
return command_fail(cmd, FUND_CANNOT_AFFORD,
"Provided PSBT cannot afford funding of "
"amount %s. %s",
2021-01-20 01:39:35 +00:00
type_to_string(tmpctx,
struct amount_sat,
amount),
type_to_string(tmpctx,
struct wally_psbt,
psbt));
res = init_set_feerate(cmd, &feerate_per_kw, &feerate_per_kw_funding);
if (res)
return res;
if (!topology_synced(cmd->ld->topology)) {
return command_fail(cmd, FUNDING_STILL_SYNCING_BITCOIN,
"Still syncing with bitcoin network");
}
peer = peer_by_id(cmd->ld, id);
if (!peer) {
return command_fail(cmd, FUNDING_UNKNOWN_PEER, "Unknown peer");
}
if (!feature_negotiated(cmd->ld->our_features,
peer->their_features,
OPT_DUAL_FUND)) {
return command_fail(cmd, FUNDING_V2_NOT_SUPPORTED,
"v2 openchannel not supported "
"by peer");
}
/* BOLT #2:
* - if both nodes advertised `option_support_large_channel`:
* - MAY set `funding_satoshis` greater than or equal to 2^24 satoshi.
* - otherwise:
* - MUST set `funding_satoshis` to less than 2^24 satoshi.
*/
if (!feature_negotiated(cmd->ld->our_features,
peer->their_features, OPT_LARGE_CHANNELS)
&& amount_sat_greater(*amount, chainparams->max_funding))
return command_fail(cmd, FUND_MAX_EXCEEDED,
"Amount exceeded %s",
type_to_string(tmpctx, struct amount_sat,
&chainparams->max_funding));
/* Add serials to any input that's missing them */
psbt_add_serials(psbt, TX_INITIATOR);
/* We require the PSBT to meet certain criteria such as
* extra, proprietary fields (`serial_id`s) or
* to have a `redeemscripts` iff the inputs are P2SH.
*
* Since this is externally provided, we confirm that
* they've done the right thing / haven't lost any required info.
*/
if (!psbt_has_required_fields(psbt))
return command_fail(cmd, FUNDING_PSBT_INVALID,
"PSBT is missing required fields %s",
type_to_string(tmpctx, struct wally_psbt,
psbt));
lightningd: fix incorrect reuse of dualopend, leading to dev_queryfeerates race CI hit this issue where it would get a tx_abort in fundchannel. This happens when the dualopend we use to query the feerates has not exited yet (it waits for the tx_abort reply), and we mistakenly reuse it. With multi-channel support, this is wrong: just run another one and it all Just Works. This means we need to rework our dual_open_control.c logic, since it would previously create an unsaved channel then not clean up if something went wrong. Most people will never try to negotiate opening multiple channels to the same peer at the same time (vs. having an established channel and opening a new one), so this case is a bit weird. ``` rates = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) # l1 leases a channel from l2 l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, feerate='{}perkw'.format(feerate), > compact_lease=rates['compact_lease']) tests/test_opening.py:1611: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ contrib/pyln-client/pyln/client/lightning.py:833: in fundchannel return self.call("fundchannel", payload) contrib/pyln-testing/pyln/testing/utils.py:721: in call res = LightningRpc.call(self, method, payload, cmdprefix, filter) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <pyln.testing.utils.PrettyPrintingLightningRpc object at 0x7f6cbcd97950> method = 'fundchannel' payload = {'amount': 500000, 'announce': True, 'compact_lease': '029a00640064000000644c4b40', 'feerate': '2000perkw', ...} cmdprefix = None, filter = None def call(self, method, payload=None, cmdprefix=None, filter=None): """Generic call API: you can set cmdprefix here, or set self.cmdprefix ... if not isinstance(resp, dict): raise ValueError("Malformed response, response is not a dictionary %s." % resp) elif "error" in resp: > raise RpcError(method, payload, resp['error']) E pyln.client.lightning.RpcError: RPC call failed: method: fundchannel, payload: {'id': '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59', 'amount': 500000, 'feerate': '2000perkw', 'announce': True, 'request_amt': 500000, 'compact_lease': '029a00640064000000644c4b40'}, error: {'code': -1, 'message': 'Abort requested', 'data': {'id': '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59', 'method': 'openchannel_init'}} ``` Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-05-25 02:36:27 +01:00
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) {
return command_fail(cmd, FUND_MAX_EXCEEDED,
"Failed to create socketpair: %s",
strerror(errno));
}
/* Now we can't fail, create channel */
channel = new_unsaved_channel(peer,
peer->ld->config.fee_base,
peer->ld->config.fee_per_satoshi);
/* We derive initial channel_id *now*, so we can tell it to connectd. */
derive_tmp_channel_id(&channel->cid,
&channel->local_basepoints.revocation);
/* Get a new open_attempt going */
channel->opener = LOCAL;
channel->open_attempt = oa = new_channel_open_attempt(channel);
channel->channel_flags = OUR_CHANNEL_FLAGS;
oa->funding = *amount;
oa->cmd = cmd;
if (!*announce_channel) {
channel->channel_flags &= ~CHANNEL_FLAGS_ANNOUNCE_CHANNEL;
log_info(peer->ld->log,
"Will open private channel with node %s",
type_to_string(tmpctx, struct node_id, id));
}
/* Needs to be stolen away from cmd */
if (our_upfront_shutdown_script)
oa->our_upfront_shutdown_script
= tal_steal(oa, our_upfront_shutdown_script);
2021-11-04 17:48:43 +00:00
/* Determine the wallet index for our_upfront_shutdown_script,
* NULL if not found. */
u32 found_wallet_index;
bool is_p2sh;
if (wallet_can_spend(cmd->ld->wallet,
oa->our_upfront_shutdown_script,
&found_wallet_index,
&is_p2sh)) {
our_upfront_shutdown_script_wallet_index = tal(tmpctx, u32);
*our_upfront_shutdown_script_wallet_index = found_wallet_index;
} else
our_upfront_shutdown_script_wallet_index = NULL;
oa->open_msg = towire_dualopend_opener_init(oa,
psbt, *amount,
oa->our_upfront_shutdown_script,
2021-11-04 17:48:43 +00:00
our_upfront_shutdown_script_wallet_index,
*feerate_per_kw,
*feerate_per_kw_funding,
channel->channel_flags,
amount_sat_zero(*request_amt) ?
NULL : request_amt,
get_block_height(cmd->ld->topology),
false,
rates);
/* Start dualopend! */
if (!peer_start_dualopend(peer, new_peer_fd(cmd, fds[0]), channel)) {
close(fds[1]);
/* FIXME: gets completed by failure path above! */
return command_its_complicated("completed by peer_start_dualopend");
}
/* Go! */
subd_send_msg(channel->owner, channel->open_attempt->open_msg);
/* Tell connectd connect this to this channel id. */
subd_send_msg(peer->ld->connectd,
take(towire_connectd_peer_connect_subd(NULL,
&peer->id,
peer->connectd_counter,
&channel->cid)));
subd_send_fd(peer->ld->connectd, fds[1]);
return command_still_pending(cmd);
}
static void psbt_request_valid(struct psbt_validator *pv)
{
struct subd *dualopend = pv->channel->owner;
if (!dualopend)
goto done;
assert(!pv->cmd);
subd_send_msg(dualopend,
take(towire_dualopend_validate_inputs_reply(NULL)));
done:
tal_free(pv);
}
static void psbt_request_invalid(struct psbt_validator *pv, const char *err_msg)
{
struct subd *dualopend = pv->channel->owner;
if (!dualopend)
goto done;
assert(!pv->cmd);
subd_send_msg(dualopend,
take(towire_dualopend_fail(NULL, err_msg)));
done:
tal_free(pv);
}
static void handle_validate_inputs(struct subd *dualopend,
const u8 *msg)
{
struct psbt_validator *pv;
pv = tal(NULL, struct psbt_validator);
if (!fromwire_dualopend_validate_inputs(pv, msg,
&pv->psbt,
&pv->role_to_validate)) {
channel_internal_error(dualopend->channel,
"Bad DUALOPEND_VALIDATE_INPUTS: %s",
tal_hex(msg, msg));
return;
}
log_debug(dualopend->ld->log,
"validating psbt for role: %s",
pv->role_to_validate == TX_INITIATOR ?
"initiator" : "accepter");
pv->cmd = NULL;
pv->channel = dualopend->channel;
pv->next_index = 0;
pv->success = psbt_request_valid;
pv->invalid_input = psbt_request_invalid;
validate_input_unspent(NULL, NULL, pv);
}
2020-12-10 20:25:54 +00:00
static void
channel_fail_fallen_behind(struct subd* dualopend, const u8 *msg)
{
struct channel *channel = dualopend->channel;
2020-12-10 20:25:54 +00:00
if (!fromwire_dualopend_fail_fallen_behind(msg)) {
channel_internal_error(channel,
"Bad DUALOPEND_FAIL_FALLEN_BEHIND: %s",
2020-12-10 20:25:54 +00:00
tal_hex(tmpctx, msg));
return;
}
channel_fallen_behind(channel, msg);
}
static void handle_psbt_changed(struct subd *dualopend,
struct channel *channel,
const u8 *msg)
{
struct channel_id cid;
u64 funding_serial;
struct wally_psbt *psbt;
struct json_stream *response;
struct openchannel2_psbt_payload *payload;
struct open_attempt *oa;
struct command *cmd;
assert(channel->open_attempt);
oa = channel->open_attempt;
cmd = oa->cmd;
if (!fromwire_dualopend_psbt_changed(tmpctx, msg,
&cid,
&channel->req_confirmed_ins[REMOTE],
&funding_serial,
&psbt)) {
channel_internal_error(channel,
"Bad DUALOPEND_PSBT_CHANGED: %s",
tal_hex(tmpctx, msg));
return;
}
switch (oa->role) {
case TX_INITIATOR:
if (!cmd) {
channel_err_broken(channel,
tal_fmt(tmpctx, "Unexpected"
" PSBT_CHANGED %s",
tal_hex(tmpctx, msg)));
return;
}
/* This might be the first time we learn the channel_id */
channel->cid = cid;
response = json_stream_success(cmd);
json_add_string(response, "channel_id",
type_to_string(tmpctx, struct channel_id,
&channel->cid));
json_add_psbt(response, "psbt", psbt);
json_add_bool(response, "commitments_secured", false);
json_add_u64(response, "funding_serial", funding_serial);
json_add_bool(response, "requires_confirmed_inputs",
channel->req_confirmed_ins[REMOTE]);
oa->cmd = NULL;
was_pending(command_success(cmd, response));
return;
case TX_ACCEPTER:
payload = tal(dualopend, struct openchannel2_psbt_payload);
payload->dualopend = dualopend;
tal_add_destructor2(dualopend,
openchannel2_psbt_remove_dualopend,
payload);
payload->psbt = tal_steal(payload, psbt);
payload->channel = channel;
plugin_hook_call_openchannel2_changed(dualopend->ld, NULL, payload);
return;
}
abort();
}
static void handle_commit_received(struct subd *dualopend,
struct channel *channel,
const u8 *msg)
{
struct lightningd *ld = dualopend->ld;
struct open_attempt *oa = channel->open_attempt;
struct channel_info channel_info;
struct bitcoin_tx *remote_commit;
struct bitcoin_signature remote_commit_sig;
struct bitcoin_outpoint funding;
u16 lease_chan_max_ppt;
u32 feerate_funding, feerate_commitment, lease_expiry,
lease_chan_max_msat, lease_blockheight_start;
struct amount_sat total_funding, funding_ours, lease_fee, lease_amt;
u8 *remote_upfront_shutdown_script,
*local_upfront_shutdown_script;
struct penalty_base *pbase;
struct wally_psbt *psbt;
struct json_stream *response;
struct openchannel2_psbt_payload *payload;
struct channel_inflight *inflight;
struct command *cmd = oa->cmd;
struct channel_type *channel_type;
secp256k1_ecdsa_signature *lease_commit_sig;
if (!fromwire_dualopend_commit_rcvd(tmpctx, msg,
&channel_info.their_config,
&remote_commit,
&pbase,
&remote_commit_sig,
&psbt,
&channel_info.theirbase.revocation,
&channel_info.theirbase.payment,
&channel_info.theirbase.htlc,
&channel_info.theirbase.delayed_payment,
&channel_info.remote_per_commit,
&channel_info.remote_fundingkey,
&funding,
&total_funding,
&funding_ours,
&channel->channel_flags,
&feerate_funding,
&feerate_commitment,
&local_upfront_shutdown_script,
&remote_upfront_shutdown_script,
&lease_amt,
&lease_blockheight_start,
&lease_expiry,
&lease_fee,
&lease_commit_sig,
&lease_chan_max_msat,
&lease_chan_max_ppt,
&channel_type)) {
channel_internal_error(channel,
"Bad WIRE_DUALOPEND_COMMIT_RCVD: %s",
tal_hex(msg, msg));
channel->open_attempt = tal_free(channel->open_attempt);
notify_channel_open_failed(channel->peer->ld, &channel->cid);
return;
}
/* We need to update the channel reserve on the config */
channel_update_reserve(channel,
&channel_info.their_config,
total_funding);
if (channel->state == DUALOPEND_OPEN_INIT) {
if (!(inflight = wallet_commit_channel(ld, channel,
remote_commit,
&remote_commit_sig,
&funding,
total_funding,
funding_ours,
&channel_info,
feerate_funding,
feerate_commitment,
oa->role == TX_INITIATOR ?
oa->our_upfront_shutdown_script :
local_upfront_shutdown_script,
remote_upfront_shutdown_script,
psbt,
lease_amt,
lease_blockheight_start,
lease_expiry,
lease_fee,
lease_commit_sig,
lease_chan_max_msat,
lease_chan_max_ppt,
channel_type))) {
channel_internal_error(channel,
"wallet_commit_channel failed"
" (chan %s)",
type_to_string(tmpctx,
struct channel_id,
&channel->cid));
channel->open_attempt
= tal_free(channel->open_attempt);
return;
}
/* FIXME: handle RBF pbases */
if (pbase)
wallet_penalty_base_add(ld->wallet,
channel->dbid,
pbase);
} else {
assert(channel->state == DUALOPEND_AWAITING_LOCKIN);
if (!(inflight = wallet_update_channel(ld, channel,
remote_commit,
&remote_commit_sig,
&funding,
total_funding,
funding_ours,
feerate_funding,
psbt,
lease_expiry,
lease_fee,
lease_commit_sig,
lease_chan_max_msat,
lease_chan_max_ppt,
lease_blockheight_start,
lease_amt))) {
channel_internal_error(channel,
"wallet_update_channel failed"
" (chan %s)",
type_to_string(tmpctx,
struct channel_id,
&channel->cid));
channel->open_attempt
= tal_free(channel->open_attempt);
return;
}
}
switch (oa->role) {
case TX_INITIATOR:
if (!oa->cmd) {
channel_err_broken(channel,
"Unexpected COMMIT_RCVD %s",
tal_hex(msg, msg));
channel->open_attempt
= tal_free(channel->open_attempt);
return;
}
response = json_stream_success(oa->cmd);
json_add_string(response, "channel_id",
type_to_string(tmpctx,
struct channel_id,
&channel->cid));
json_add_psbt(response, "psbt", psbt);
json_add_bool(response, "commitments_secured", true);
/* For convenience sake, we include the funding outnum */
json_add_num(response, "funding_outnum", funding.n);
if (oa->our_upfront_shutdown_script) {
json_add_hex_talarr(response, "close_to",
oa->our_upfront_shutdown_script);
/* FIXME: also include the output as address */
}
channel->open_attempt
= tal_free(channel->open_attempt);
was_pending(command_success(cmd, response));
return;
case TX_ACCEPTER:
payload = tal(dualopend, struct openchannel2_psbt_payload);
payload->ld = ld;
payload->dualopend = dualopend;
tal_add_destructor2(dualopend,
openchannel2_psbt_remove_dualopend,
payload);
payload->channel = channel;
payload->psbt = clone_psbt(payload, inflight->funding_psbt);
channel->open_attempt
= tal_free(channel->open_attempt);
/* We don't have a command, so set to NULL here */
payload->channel->openchannel_signed_cmd = NULL;
/* We call out to hook who will
* provide signatures for us! */
plugin_hook_call_openchannel2_sign(ld, NULL, payload);
return;
}
abort();
}
static unsigned int dual_opend_msg(struct subd *dualopend,
const u8 *msg, const int *fds)
{
enum dualopend_wire t = fromwire_peektype(msg);
struct channel *channel = dualopend->channel;
switch (t) {
case WIRE_DUALOPEND_GOT_OFFER:
accepter_got_offer(dualopend, dualopend->channel, msg);
return 0;
case WIRE_DUALOPEND_GOT_RBF_OFFER:
rbf_got_offer(dualopend, msg);
return 0;
case WIRE_DUALOPEND_PSBT_CHANGED:
handle_psbt_changed(dualopend, channel, msg);
return 0;
case WIRE_DUALOPEND_COMMIT_RCVD:
handle_commit_received(dualopend, channel, msg);
return 0;
case WIRE_DUALOPEND_RBF_VALIDATE:
handle_validate_rbf(dualopend, msg);
return 0;
case WIRE_DUALOPEND_VALIDATE_LEASE:
handle_validate_lease(dualopend, msg);
return 0;
case WIRE_DUALOPEND_FUNDING_SIGS:
handle_peer_tx_sigs_msg(dualopend, msg);
return 0;
case WIRE_DUALOPEND_TX_SIGS_SENT:
handle_peer_tx_sigs_sent(dualopend, fds, msg);
return 0;
2020-12-10 20:25:54 +00:00
case WIRE_DUALOPEND_PEER_LOCKED:
handle_peer_locked(dualopend, msg);
return 0;
case WIRE_DUALOPEND_DRY_RUN:
handle_dry_run_finished(dualopend, msg);
return 0;
case WIRE_DUALOPEND_CHANNEL_LOCKED:
if (tal_count(fds) != 1)
return 1;
handle_channel_locked(dualopend, fds, msg);
return 0;
case WIRE_DUALOPEND_GOT_SHUTDOWN:
handle_peer_wants_to_close(dualopend, msg);
return 0;
case WIRE_DUALOPEND_SHUTDOWN_COMPLETE:
if (tal_count(fds) != 1)
return 1;
handle_channel_closed(dualopend, fds, msg);
return 0;
2020-12-10 20:25:54 +00:00
case WIRE_DUALOPEND_FAIL_FALLEN_BEHIND:
channel_fail_fallen_behind(dualopend, msg);
return 0;
case WIRE_DUALOPEND_LOCAL_PRIVATE_CHANNEL:
handle_local_private_channel(dualopend, msg);
return 0;
case WIRE_DUALOPEND_VALIDATE_INPUTS:
handle_validate_inputs(dualopend, msg);
return 0;
/* Messages we send */
case WIRE_DUALOPEND_INIT:
case WIRE_DUALOPEND_REINIT:
case WIRE_DUALOPEND_OPENER_INIT:
case WIRE_DUALOPEND_RBF_INIT:
case WIRE_DUALOPEND_GOT_OFFER_REPLY:
case WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY:
case WIRE_DUALOPEND_VALIDATE_INPUTS_REPLY:
case WIRE_DUALOPEND_RBF_VALID:
case WIRE_DUALOPEND_VALIDATE_LEASE_REPLY:
case WIRE_DUALOPEND_FAIL:
case WIRE_DUALOPEND_PSBT_UPDATED:
case WIRE_DUALOPEND_SEND_TX_SIGS:
case WIRE_DUALOPEND_SEND_SHUTDOWN:
case WIRE_DUALOPEND_DEPTH_REACHED:
case WIRE_DUALOPEND_DEV_MEMLEAK:
case WIRE_DUALOPEND_DEV_MEMLEAK_REPLY:
break;
}
log_broken(dualopend->log, "Unexpected msg %s: %s",
dualopend_wire_name(t), tal_hex(tmpctx, msg));
tal_free(dualopend);
return 0;
}
#if DEVELOPER
static struct command_result *json_queryrates(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct node_id *id;
struct peer *peer;
struct channel *channel;
u32 *feerate_per_kw_funding;
u32 *feerate_per_kw;
struct amount_sat *amount, *request_amt;
struct wally_psbt *psbt;
struct open_attempt *oa;
2021-11-04 17:48:43 +00:00
u32 *our_upfront_shutdown_script_wallet_index;
struct command_result *res;
int fds[2];
if (!param(cmd, buffer, params,
p_req("id", param_node_id, &id),
p_req("amount", param_sat, &amount),
p_req("request_amt", param_sat, &request_amt),
p_opt("commitment_feerate", param_feerate, &feerate_per_kw),
p_opt("funding_feerate", param_feerate, &feerate_per_kw_funding),
NULL))
return command_param_failed();
res = init_set_feerate(cmd, &feerate_per_kw, &feerate_per_kw_funding);
if (res)
return res;
peer = peer_by_id(cmd->ld, id);
if (!peer) {
return command_fail(cmd, FUNDING_UNKNOWN_PEER, "Unknown peer");
}
if (peer->connected != PEER_CONNECTED)
return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED,
"Peer %s",
peer->connected == PEER_DISCONNECTED
? "not connected" : "still connecting");
lightningd: fix incorrect reuse of dualopend, leading to dev_queryfeerates race CI hit this issue where it would get a tx_abort in fundchannel. This happens when the dualopend we use to query the feerates has not exited yet (it waits for the tx_abort reply), and we mistakenly reuse it. With multi-channel support, this is wrong: just run another one and it all Just Works. This means we need to rework our dual_open_control.c logic, since it would previously create an unsaved channel then not clean up if something went wrong. Most people will never try to negotiate opening multiple channels to the same peer at the same time (vs. having an established channel and opening a new one), so this case is a bit weird. ``` rates = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) # l1 leases a channel from l2 l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, feerate='{}perkw'.format(feerate), > compact_lease=rates['compact_lease']) tests/test_opening.py:1611: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ contrib/pyln-client/pyln/client/lightning.py:833: in fundchannel return self.call("fundchannel", payload) contrib/pyln-testing/pyln/testing/utils.py:721: in call res = LightningRpc.call(self, method, payload, cmdprefix, filter) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <pyln.testing.utils.PrettyPrintingLightningRpc object at 0x7f6cbcd97950> method = 'fundchannel' payload = {'amount': 500000, 'announce': True, 'compact_lease': '029a00640064000000644c4b40', 'feerate': '2000perkw', ...} cmdprefix = None, filter = None def call(self, method, payload=None, cmdprefix=None, filter=None): """Generic call API: you can set cmdprefix here, or set self.cmdprefix ... if not isinstance(resp, dict): raise ValueError("Malformed response, response is not a dictionary %s." % resp) elif "error" in resp: > raise RpcError(method, payload, resp['error']) E pyln.client.lightning.RpcError: RPC call failed: method: fundchannel, payload: {'id': '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59', 'amount': 500000, 'feerate': '2000perkw', 'announce': True, 'request_amt': 500000, 'compact_lease': '029a00640064000000644c4b40'}, error: {'code': -1, 'message': 'Abort requested', 'data': {'id': '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59', 'method': 'openchannel_init'}} ``` Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-05-25 02:36:27 +01:00
channel = new_unsaved_channel(peer,
peer->ld->config.fee_base,
peer->ld->config.fee_per_satoshi);
lightningd: fix incorrect reuse of dualopend, leading to dev_queryfeerates race CI hit this issue where it would get a tx_abort in fundchannel. This happens when the dualopend we use to query the feerates has not exited yet (it waits for the tx_abort reply), and we mistakenly reuse it. With multi-channel support, this is wrong: just run another one and it all Just Works. This means we need to rework our dual_open_control.c logic, since it would previously create an unsaved channel then not clean up if something went wrong. Most people will never try to negotiate opening multiple channels to the same peer at the same time (vs. having an established channel and opening a new one), so this case is a bit weird. ``` rates = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) # l1 leases a channel from l2 l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, feerate='{}perkw'.format(feerate), > compact_lease=rates['compact_lease']) tests/test_opening.py:1611: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ contrib/pyln-client/pyln/client/lightning.py:833: in fundchannel return self.call("fundchannel", payload) contrib/pyln-testing/pyln/testing/utils.py:721: in call res = LightningRpc.call(self, method, payload, cmdprefix, filter) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <pyln.testing.utils.PrettyPrintingLightningRpc object at 0x7f6cbcd97950> method = 'fundchannel' payload = {'amount': 500000, 'announce': True, 'compact_lease': '029a00640064000000644c4b40', 'feerate': '2000perkw', ...} cmdprefix = None, filter = None def call(self, method, payload=None, cmdprefix=None, filter=None): """Generic call API: you can set cmdprefix here, or set self.cmdprefix ... if not isinstance(resp, dict): raise ValueError("Malformed response, response is not a dictionary %s." % resp) elif "error" in resp: > raise RpcError(method, payload, resp['error']) E pyln.client.lightning.RpcError: RPC call failed: method: fundchannel, payload: {'id': '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59', 'amount': 500000, 'feerate': '2000perkw', 'announce': True, 'request_amt': 500000, 'compact_lease': '029a00640064000000644c4b40'}, error: {'code': -1, 'message': 'Abort requested', 'data': {'id': '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59', 'method': 'openchannel_init'}} ``` Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2023-05-25 02:36:27 +01:00
/* We derive initial channel_id *now*, so we can tell it to
* connectd. */
derive_tmp_channel_id(&channel->cid,
&channel->local_basepoints.revocation);
if (!feature_negotiated(cmd->ld->our_features,
peer->their_features,
OPT_DUAL_FUND)) {
return command_fail(cmd, FUNDING_V2_NOT_SUPPORTED,
"v2 openchannel not supported "
"by peer, can't query rates");
}
/* BOLT #2:
* - if both nodes advertised `option_support_large_channel`:
* - MAY set `funding_satoshis` greater than or equal to 2^24 satoshi.
* - otherwise:
* - MUST set `funding_satoshis` to less than 2^24 satoshi.
*/
if (!feature_negotiated(cmd->ld->our_features,
peer->their_features, OPT_LARGE_CHANNELS)
&& amount_sat_greater(*amount, chainparams->max_funding))
return command_fail(cmd, FUND_MAX_EXCEEDED,
"Amount exceeded %s",
type_to_string(tmpctx, struct amount_sat,
&chainparams->max_funding));
/* Get a new open_attempt going, keeps us from re-initing
* while looking */
channel->opener = LOCAL;
channel->open_attempt = oa = new_channel_open_attempt(channel);
channel->channel_flags = OUR_CHANNEL_FLAGS;
oa->funding = *amount;
oa->cmd = cmd;
/* empty psbt to start */
psbt = create_psbt(tmpctx, 0, 0, 0);
2021-11-04 17:48:43 +00:00
/* Determine the wallet index for our_upfront_shutdown_script,
* NULL if not found. */
u32 found_wallet_index;
bool is_p2sh;
if (wallet_can_spend(cmd->ld->wallet,
oa->our_upfront_shutdown_script,
&found_wallet_index,
&is_p2sh)) {
our_upfront_shutdown_script_wallet_index = tal(tmpctx, u32);
*our_upfront_shutdown_script_wallet_index = found_wallet_index;
} else
our_upfront_shutdown_script_wallet_index = NULL;
oa->open_msg = towire_dualopend_opener_init(oa,
psbt, *amount,
oa->our_upfront_shutdown_script,
2021-11-04 17:48:43 +00:00
our_upfront_shutdown_script_wallet_index,
*feerate_per_kw,
*feerate_per_kw_funding,
channel->channel_flags,
amount_sat_zero(*request_amt) ?
NULL : request_amt,
get_block_height(cmd->ld->topology),
true,
NULL);
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) {
return command_fail(cmd, FUND_MAX_EXCEEDED,
"Failed to create socketpair: %s",
strerror(errno));
}
/* Start dualopend! */
if (!peer_start_dualopend(peer, new_peer_fd(cmd, fds[0]), channel)) {
close(fds[1]);
/* FIXME: gets completed by failure path above! */
return command_its_complicated("completed by peer_start_dualopend");
}
/* Go! */
subd_send_msg(channel->owner, channel->open_attempt->open_msg);
/* Tell connectd connect this to this channel id. */
subd_send_msg(peer->ld->connectd,
take(towire_connectd_peer_connect_subd(NULL,
&peer->id,
peer->connectd_counter,
&channel->cid)));
subd_send_fd(peer->ld->connectd, fds[1]);
return command_still_pending(cmd);
}
static const struct json_command queryrates_command = {
"dev-queryrates",
"channels",
json_queryrates,
"Ask a peer what their contribution and liquidity rates are"
" for the given {amount} and {requested_amt}"
};
AUTODATA(json_command, &queryrates_command);
#endif /* DEVELOPER */
static const struct json_command openchannel_init_command = {
"openchannel_init",
"channels",
json_openchannel_init,
"Init an open channel to {id} with {initialpsbt} for {amount} satoshis. "
"Returns updated {psbt} with (partial) contributions from peer"
};
static const struct json_command openchannel_update_command = {
"openchannel_update",
"channels",
json_openchannel_update,
"Update {channel_id} with {psbt}. "
"Returns updated {psbt} with (partial) contributions from peer. "
"If {commitments_secured} is true, next call should be to openchannel_signed"
};
static const struct json_command openchannel_signed_command = {
"openchannel_signed",
"channels",
json_openchannel_signed,
"Send our {signed_psbt}'s tx sigs for {channel_id}."
};
static const struct json_command openchannel_bump_command = {
"openchannel_bump",
"channels",
json_openchannel_bump,
"Attempt to bump the fee on {channel_id}'s funding transaction."
};
static const struct json_command openchannel_abort_command = {
"openchannel_abort",
"channels",
json_openchannel_abort,
"Abort {channel_id}'s open. Usable while `commitment_signed=false`."
};
AUTODATA(json_command, &openchannel_init_command);
AUTODATA(json_command, &openchannel_update_command);
AUTODATA(json_command, &openchannel_signed_command);
AUTODATA(json_command, &openchannel_bump_command);
AUTODATA(json_command, &openchannel_abort_command);
bool peer_start_dualopend(struct peer *peer,
struct peer_fd *peer_fd,
struct channel *channel)
{
int hsmfd;
u32 max_to_self_delay;
struct amount_msat min_effective_htlc_capacity;
const u8 *msg;
hsmfd = hsm_get_client_fd(peer->ld, &peer->id, channel->unsaved_dbid,
HSM_CAP_COMMITMENT_POINT
| HSM_CAP_SIGN_REMOTE_TX
| HSM_CAP_SIGN_WILL_FUND_OFFER);
channel->owner = new_channel_subd(channel,
peer->ld,
"lightning_dualopend",
channel,
&peer->id,
channel->log, true,
dualopend_wire_name,
dual_opend_msg,
channel_errmsg,
channel_set_billboard,
2022-01-11 01:13:59 +00:00
take(&peer_fd->fd),
take(&hsmfd), NULL);
if (!channel->owner) {
channel_internal_error(channel,
"Running lightningd_dualopend: %s",
strerror(errno));
return false;
}
channel_config(peer->ld, &channel->our_config,
&max_to_self_delay,
&min_effective_htlc_capacity);
/* BOLT #2:
*
* The sender:
* - if `channel_type` includes `option_zeroconf`:
* - MUST set `minimum_depth` to zero.
* - otherwise:
* - SHOULD set `minimum_depth` to a number of blocks it
* considers reasonable to avoid double-spending of the
* funding transaction.
*/
/* FIXME: We should override this to 0 in the openchannel2 hook of we want zeroconf*/
channel->minimum_depth = peer->ld->config.anchor_confirms;
msg = towire_dualopend_init(NULL, chainparams,
peer->ld->our_features,
peer->their_features,
&channel->our_config,
max_to_self_delay,
min_effective_htlc_capacity,
&channel->local_basepoints,
&channel->local_funding_pubkey,
channel->minimum_depth,
peer->ld->config.require_confirmed_inputs);
subd_send_msg(channel->owner, take(msg));
return true;
}
bool peer_restart_dualopend(struct peer *peer,
2022-01-11 01:13:59 +00:00
struct peer_fd *peer_fd,
struct channel *channel)
{
u32 max_to_self_delay, blockheight;
struct amount_msat min_effective_htlc_capacity;
struct channel_config unused_config;
struct channel_inflight *inflight;
int hsmfd;
2021-11-04 17:48:43 +00:00
u32 *local_shutdown_script_wallet_index;
u8 *msg;
if (channel_unsaved(channel))
return peer_start_dualopend(peer, peer_fd, channel);
hsmfd = hsm_get_client_fd(peer->ld, &peer->id, channel->dbid,
HSM_CAP_COMMITMENT_POINT
| HSM_CAP_SIGN_REMOTE_TX
| HSM_CAP_SIGN_WILL_FUND_OFFER);
channel_set_owner(channel,
new_channel_subd(channel, peer->ld,
"lightning_dualopend",
channel,
&peer->id,
channel->log, true,
dualopend_wire_name,
dual_opend_msg,
channel_errmsg,
channel_set_billboard,
2022-01-11 01:13:59 +00:00
take(&peer_fd->fd),
take(&hsmfd), NULL));
if (!channel->owner) {
log_broken(channel->log, "Could not subdaemon channel: %s",
strerror(errno));
/* Disconnect it. */
subd_send_msg(peer->ld->connectd,
take(towire_connectd_discard_peer(NULL, &channel->peer->id,
channel->peer->connectd_counter)));
return false;
}
/* Find the max self delay and min htlc capacity */
channel_config(peer->ld, &unused_config,
&max_to_self_delay,
&min_effective_htlc_capacity);
inflight = channel_current_inflight(channel);
assert(inflight);
blockheight = get_blockheight(channel->blockheight_states,
channel->opener, LOCAL);
2021-11-04 17:48:43 +00:00
/* Determine the wallet index for the LOCAL shutdown_scriptpubkey,
* NULL if not found. */
u32 found_wallet_index;
bool is_p2sh;
if (wallet_can_spend(peer->ld->wallet,
channel->shutdown_scriptpubkey[LOCAL],
&found_wallet_index,
&is_p2sh)) {
local_shutdown_script_wallet_index = tal(tmpctx, u32);
*local_shutdown_script_wallet_index = found_wallet_index;
} else
local_shutdown_script_wallet_index = NULL;
msg = towire_dualopend_reinit(NULL,
chainparams,
peer->ld->our_features,
peer->their_features,
&channel->our_config,
&channel->channel_info.their_config,
&channel->cid,
max_to_self_delay,
min_effective_htlc_capacity,
&channel->local_basepoints,
&channel->local_funding_pubkey,
&channel->channel_info.remote_fundingkey,
channel->minimum_depth,
&inflight->funding->outpoint,
inflight->funding->feerate,
channel->funding_sats,
channel->our_msat,
&channel->channel_info.theirbase,
&channel->channel_info.remote_per_commit,
inflight->funding_psbt,
channel->opener,
channel->scid != NULL,
channel->remote_channel_ready,
channel->state == CHANNELD_SHUTTING_DOWN,
channel->shutdown_scriptpubkey[REMOTE] != NULL,
channel->shutdown_scriptpubkey[LOCAL],
channel->remote_upfront_shutdown_script,
2021-11-04 17:48:43 +00:00
local_shutdown_script_wallet_index,
inflight->remote_tx_sigs,
channel->fee_states,
channel->channel_flags,
blockheight,
inflight->lease_expiry,
inflight->lease_commit_sig,
inflight->lease_chan_max_msat,
inflight->lease_chan_max_ppt,
amount_sat_zero(inflight->lease_amt) ?
NULL : &inflight->lease_amt,
channel->type,
channel->req_confirmed_ins[LOCAL],
channel->req_confirmed_ins[REMOTE]);
subd_send_msg(channel->owner, take(msg));
return true;
}