mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 13:25:43 +01:00
openingd/: Fail fundchannel_start
if we already are, or will become, the fundee.
Fixes: #4108 Changelog-Fixed: Network: Fixed a race condition when us and a peer attempt to make channels to each other at nearly the same time.
This commit is contained in:
parent
712e0796fc
commit
0fd87b85da
@ -55,6 +55,8 @@ new_uncommitted_channel(struct peer *peer)
|
|||||||
uc->peer->uncommitted_channel = uc;
|
uc->peer->uncommitted_channel = uc;
|
||||||
tal_add_destructor(uc, destroy_uncommitted_channel);
|
tal_add_destructor(uc, destroy_uncommitted_channel);
|
||||||
|
|
||||||
|
uc->got_offer = false;
|
||||||
|
|
||||||
return uc;
|
return uc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,11 @@ struct uncommitted_channel {
|
|||||||
/* Public key for funding tx. */
|
/* Public key for funding tx. */
|
||||||
struct pubkey local_funding_pubkey;
|
struct pubkey local_funding_pubkey;
|
||||||
|
|
||||||
|
/* If true, we are already in fundee-mode and any future
|
||||||
|
* `fundchannel_start` on our end should fail.
|
||||||
|
*/
|
||||||
|
bool got_offer;
|
||||||
|
|
||||||
/* These are *not* filled in by new_uncommitted_channel: */
|
/* These are *not* filled in by new_uncommitted_channel: */
|
||||||
|
|
||||||
/* Minimum funding depth (if opener == REMOTE). */
|
/* Minimum funding depth (if opener == REMOTE). */
|
||||||
|
@ -104,6 +104,11 @@ wallet_commit_channel(struct lightningd *ld,
|
|||||||
bool option_static_remotekey;
|
bool option_static_remotekey;
|
||||||
bool option_anchor_outputs;
|
bool option_anchor_outputs;
|
||||||
|
|
||||||
|
/* We cannot both be the fundee *and* have a `fundchannel_start`
|
||||||
|
* command running!
|
||||||
|
*/
|
||||||
|
assert(!(uc->got_offer && uc->fc));
|
||||||
|
|
||||||
/* Get a key to use for closing outputs from this tx */
|
/* Get a key to use for closing outputs from this tx */
|
||||||
final_key_idx = wallet_get_newindex(ld);
|
final_key_idx = wallet_get_newindex(ld);
|
||||||
if (final_key_idx == -1) {
|
if (final_key_idx == -1) {
|
||||||
@ -538,6 +543,14 @@ failed:
|
|||||||
tal_free(uc);
|
tal_free(uc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* These two functions used to be a single function, pre-declaring
|
||||||
|
* here in order to reduce review load since the original function
|
||||||
|
* is not really changed at all in its operation, just need access
|
||||||
|
* to the cleanup part in a separate piece of code.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
opening_funder_failed_cancel_commands(struct uncommitted_channel *uc,
|
||||||
|
const char *desc);
|
||||||
static void opening_funder_failed(struct subd *openingd, const u8 *msg,
|
static void opening_funder_failed(struct subd *openingd, const u8 *msg,
|
||||||
struct uncommitted_channel *uc)
|
struct uncommitted_channel *uc)
|
||||||
{
|
{
|
||||||
@ -554,6 +567,16 @@ static void opening_funder_failed(struct subd *openingd, const u8 *msg,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opening_funder_failed_cancel_commands(uc, desc);
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
opening_funder_failed_cancel_commands(struct uncommitted_channel *uc,
|
||||||
|
const char *desc)
|
||||||
|
{
|
||||||
|
/* If no funding command(s) pending, do nothing. */
|
||||||
|
if (!uc->fc)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Tell anyone who was trying to cancel */
|
/* Tell anyone who was trying to cancel */
|
||||||
for (size_t i = 0; i < tal_count(uc->fc->cancels); i++) {
|
for (size_t i = 0; i < tal_count(uc->fc->cancels); i++) {
|
||||||
struct json_stream *response;
|
struct json_stream *response;
|
||||||
@ -568,12 +591,17 @@ static void opening_funder_failed(struct subd *openingd, const u8 *msg,
|
|||||||
was_pending(command_fail(uc->fc->cmd, LIGHTNINGD, "%s", desc));
|
was_pending(command_fail(uc->fc->cmd, LIGHTNINGD, "%s", desc));
|
||||||
|
|
||||||
/* Clear uc->fc, so we can try again, and so we don't fail twice
|
/* Clear uc->fc, so we can try again, and so we don't fail twice
|
||||||
* if they close. */
|
* if they close.
|
||||||
|
* This code is also used in the case where we turn out to already
|
||||||
|
* be the fundee, in which case we should not have any `fc` at all,
|
||||||
|
* so we definitely should clear this.
|
||||||
|
*/
|
||||||
uc->fc = tal_free(uc->fc);
|
uc->fc = tal_free(uc->fc);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct openchannel_hook_payload {
|
struct openchannel_hook_payload {
|
||||||
struct subd *openingd;
|
struct subd *openingd;
|
||||||
|
struct uncommitted_channel* uc;
|
||||||
struct amount_sat funding_satoshis;
|
struct amount_sat funding_satoshis;
|
||||||
struct amount_msat push_msat;
|
struct amount_msat push_msat;
|
||||||
struct amount_sat dust_limit_satoshis;
|
struct amount_sat dust_limit_satoshis;
|
||||||
@ -631,6 +659,7 @@ openchannel_hook_final(struct openchannel_hook_payload *payload STEALS)
|
|||||||
struct subd *openingd = payload->openingd;
|
struct subd *openingd = payload->openingd;
|
||||||
const u8 *our_upfront_shutdown_script = payload->our_upfront_shutdown_script;
|
const u8 *our_upfront_shutdown_script = payload->our_upfront_shutdown_script;
|
||||||
const char *errmsg = payload->errmsg;
|
const char *errmsg = payload->errmsg;
|
||||||
|
struct uncommitted_channel* uc = payload->uc;
|
||||||
|
|
||||||
/* We want to free this, whatever happens. */
|
/* We want to free this, whatever happens. */
|
||||||
tal_steal(tmpctx, payload);
|
tal_steal(tmpctx, payload);
|
||||||
@ -641,6 +670,16 @@ openchannel_hook_final(struct openchannel_hook_payload *payload STEALS)
|
|||||||
|
|
||||||
tal_del_destructor2(openingd, openchannel_payload_remove_openingd, payload);
|
tal_del_destructor2(openingd, openchannel_payload_remove_openingd, payload);
|
||||||
|
|
||||||
|
if (!errmsg) {
|
||||||
|
/* Plugins accepted the offer, cancel any of our
|
||||||
|
* funder-side commands. */
|
||||||
|
opening_funder_failed_cancel_commands(uc,
|
||||||
|
"Have in-progress "
|
||||||
|
"`open_channel` from "
|
||||||
|
"peer");
|
||||||
|
uc->got_offer = true;
|
||||||
|
}
|
||||||
|
|
||||||
subd_send_msg(openingd,
|
subd_send_msg(openingd,
|
||||||
take(towire_openingd_got_offer_reply(NULL, errmsg,
|
take(towire_openingd_got_offer_reply(NULL, errmsg,
|
||||||
our_upfront_shutdown_script)));
|
our_upfront_shutdown_script)));
|
||||||
@ -739,6 +778,7 @@ static void opening_got_offer(struct subd *openingd,
|
|||||||
|
|
||||||
payload = tal(openingd, struct openchannel_hook_payload);
|
payload = tal(openingd, struct openchannel_hook_payload);
|
||||||
payload->openingd = openingd;
|
payload->openingd = openingd;
|
||||||
|
payload->uc = uc;
|
||||||
payload->our_upfront_shutdown_script = NULL;
|
payload->our_upfront_shutdown_script = NULL;
|
||||||
payload->errmsg = NULL;
|
payload->errmsg = NULL;
|
||||||
if (!fromwire_openingd_got_offer(payload, msg,
|
if (!fromwire_openingd_got_offer(payload, msg,
|
||||||
@ -1089,6 +1129,13 @@ static struct command_result *json_fund_channel_start(struct command *cmd,
|
|||||||
return command_fail(cmd, LIGHTNINGD, "Already funding channel");
|
return command_fail(cmd, LIGHTNINGD, "Already funding channel");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (peer->uncommitted_channel->got_offer) {
|
||||||
|
return command_fail(cmd, LIGHTNINGD,
|
||||||
|
"Have in-progress "
|
||||||
|
"`open_channel` from "
|
||||||
|
"peer");
|
||||||
|
}
|
||||||
|
|
||||||
/* BOLT #2:
|
/* BOLT #2:
|
||||||
* - if both nodes advertised `option_support_large_channel`:
|
* - if both nodes advertised `option_support_large_channel`:
|
||||||
* - MAY set `funding_satoshis` greater than or equal to 2^24 satoshi.
|
* - MAY set `funding_satoshis` greater than or equal to 2^24 satoshi.
|
||||||
|
@ -2761,7 +2761,6 @@ def test_channel_opener(node_factory):
|
|||||||
assert(l2.rpc.listpeers()['peers'][0]['channels'][0]['closer'] == 'remote')
|
assert(l2.rpc.listpeers()['peers'][0]['channels'][0]['closer'] == 'remote')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(strict=True)
|
|
||||||
def test_fundchannel_start_alternate(node_factory, executor):
|
def test_fundchannel_start_alternate(node_factory, executor):
|
||||||
''' Test to see what happens if two nodes start channeling to
|
''' Test to see what happens if two nodes start channeling to
|
||||||
each other alternately.
|
each other alternately.
|
||||||
|
Loading…
Reference in New Issue
Block a user