Skip to content

Commit e1a464e

Browse files
niftyneiwhitslack
authored andcommitted
df: check mempool/block for funding output on broadcast fail
If we can't broadcast the tx, confirm that it didn't end up in the mempool or the utxo set before throwing an error. Note that this doesn't protect us in the case where the funding output has already been *spent*... but that's extremely rare, right? Fixes ElementsProject#5296 Reported-By: @rustyrussell Collab-With: @vincenzopalazzo
1 parent 87c0a22 commit e1a464e

File tree

2 files changed

+78
-39
lines changed

2 files changed

+78
-39
lines changed

lightningd/dual_open_control.c

Lines changed: 71 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,50 +1413,19 @@ static void handle_local_private_channel(struct subd *dualopend,
14131413
struct channel_send {
14141414
const struct wally_tx *wtx;
14151415
struct channel *channel;
1416+
const char *err_msg;
14161417
};
14171418

1418-
static void sendfunding_done(struct bitcoind *bitcoind UNUSED,
1419-
bool success, const char *msg,
1420-
struct channel_send *cs)
1419+
static void handle_tx_broadcast(struct channel_send *cs)
14211420
{
14221421
struct lightningd *ld = cs->channel->peer->ld;
1423-
struct channel *channel = cs->channel;
14241422
const struct wally_tx *wtx = cs->wtx;
1423+
struct channel *channel = cs->channel;
1424+
struct command *cmd = channel->openchannel_signed_cmd;
14251425
struct json_stream *response;
14261426
struct bitcoin_txid txid;
14271427
struct amount_sat unused;
14281428
int num_utxos;
1429-
struct command *cmd = channel->openchannel_signed_cmd;
1430-
channel->openchannel_signed_cmd = NULL;
1431-
1432-
if (!cmd && channel->opener == LOCAL)
1433-
log_unusual(channel->log,
1434-
"No outstanding command for channel %s,"
1435-
" funding sent was success? %d",
1436-
type_to_string(tmpctx, struct channel_id,
1437-
&channel->cid),
1438-
success);
1439-
1440-
if (!success) {
1441-
if (cmd)
1442-
was_pending(command_fail(cmd,
1443-
FUNDING_BROADCAST_FAIL,
1444-
"Error broadcasting funding "
1445-
"tx: %s. Unsent tx discarded "
1446-
"%s.",
1447-
msg,
1448-
type_to_string(tmpctx,
1449-
struct wally_tx,
1450-
wtx)));
1451-
log_unusual(channel->log,
1452-
"Error broadcasting funding "
1453-
"tx: %s. Unsent tx discarded "
1454-
"%s.",
1455-
msg,
1456-
type_to_string(tmpctx, struct wally_tx, wtx));
1457-
tal_free(cs);
1458-
return;
1459-
}
14601429

14611430
/* This might have spent UTXOs from our wallet */
14621431
num_utxos = wallet_extract_owned_outputs(ld->wallet,
@@ -1472,11 +1441,78 @@ static void sendfunding_done(struct bitcoind *bitcoind UNUSED,
14721441
json_add_txid(response, "txid", &txid);
14731442
json_add_channel_id(response, "channel_id", &channel->cid);
14741443
was_pending(command_success(cmd, response));
1444+
1445+
cs->channel->openchannel_signed_cmd = NULL;
14751446
}
1447+
}
1448+
1449+
static void check_utxo_block(struct bitcoind *bitcoind UNUSED,
1450+
const struct bitcoin_tx_output *txout,
1451+
void *arg)
1452+
{
1453+
struct channel_send *cs = arg;
1454+
struct command *cmd = cs->channel->openchannel_signed_cmd;
1455+
const struct wally_tx *wtx = cs->wtx;
1456+
1457+
/* note: if this tx has been included in a block *and spent*
1458+
* then this will also fail... */
1459+
if (!txout) {
1460+
if (cmd) {
1461+
was_pending(command_fail(cmd,
1462+
FUNDING_BROADCAST_FAIL,
1463+
"Error broadcasting funding "
1464+
"tx: %s. Unsent tx discarded "
1465+
"%s.",
1466+
cs->err_msg,
1467+
type_to_string(tmpctx,
1468+
struct wally_tx,
1469+
wtx)));
1470+
cs->channel->openchannel_signed_cmd = NULL;
1471+
}
1472+
1473+
log_unusual(cs->channel->log,
1474+
"Error broadcasting funding "
1475+
"tx: %s. Unsent tx discarded "
1476+
"%s.",
1477+
cs->err_msg,
1478+
type_to_string(tmpctx, struct wally_tx, wtx));
1479+
} else
1480+
handle_tx_broadcast(cs);
14761481

14771482
tal_free(cs);
14781483
}
14791484

1485+
static void sendfunding_done(struct bitcoind *bitcoind UNUSED,
1486+
bool success, const char *msg,
1487+
struct channel_send *cs)
1488+
{
1489+
struct lightningd *ld = cs->channel->peer->ld;
1490+
struct channel *channel = cs->channel;
1491+
struct command *cmd = channel->openchannel_signed_cmd;
1492+
1493+
if (!cmd && channel->opener == LOCAL)
1494+
log_unusual(channel->log,
1495+
"No outstanding command for channel %s,"
1496+
" funding sent was success? %d",
1497+
type_to_string(tmpctx, struct channel_id,
1498+
&channel->cid),
1499+
success);
1500+
1501+
if (success) {
1502+
handle_tx_broadcast(cs);
1503+
tal_free(cs);
1504+
} else {
1505+
/* If the tx was mined into a block, it's possible
1506+
* that the broadcast would fail. Verify that's not
1507+
* the case here. */
1508+
cs->err_msg = tal_strdup(cs, msg);
1509+
bitcoind_getutxout(ld->topology->bitcoind,
1510+
&channel->funding,
1511+
check_utxo_block,
1512+
cs);
1513+
}
1514+
}
1515+
14801516

14811517
static void send_funding_tx(struct channel *channel,
14821518
const struct wally_tx *wtx TAKES)

tests/test_connection.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2680,7 +2680,7 @@ def test_fundee_forget_funding_tx_unconfirmed(node_factory, bitcoind):
26802680
# is much slower in VALGRIND mode and wait_for_log
26812681
# could time out before lightningd processes all the
26822682
# blocks.
2683-
blocks = 200
2683+
blocks = 50
26842684
# opener
26852685
l1 = node_factory.get_node()
26862686
# peer
@@ -2689,14 +2689,16 @@ def test_fundee_forget_funding_tx_unconfirmed(node_factory, bitcoind):
26892689

26902690
# Give opener some funds.
26912691
l1.fundwallet(10**7)
2692-
# Let blocks settle.
2693-
time.sleep(1)
26942692

26952693
def mock_sendrawtransaction(r):
26962694
return {'id': r['id'], 'error': {'code': 100, 'message': 'sendrawtransaction disabled'}}
26972695

2696+
def mock_donothing(r):
2697+
return {'id': r['id'], 'result': {'success': True}}
2698+
26982699
# Prevent opener from broadcasting funding tx (any tx really).
26992700
l1.daemon.rpcproxy.mock_rpc('sendrawtransaction', mock_sendrawtransaction)
2701+
l2.daemon.rpcproxy.mock_rpc('sendrawtransaction', mock_donothing)
27002702

27012703
# Fund the channel.
27022704
# The process will complete, but opener will be unable
@@ -2708,7 +2710,8 @@ def mock_sendrawtransaction(r):
27082710
bitcoind.generate_block(blocks)
27092711

27102712
# fundee will forget channel!
2711-
l2.daemon.wait_for_log('Forgetting channel: It has been {} blocks'.format(blocks))
2713+
# (Note that we let the last number be anything (hence the {}\d)
2714+
l2.daemon.wait_for_log(r'Forgetting channel: It has been {}\d blocks'.format(str(blocks)[:-1]))
27122715

27132716
# fundee will also forget and disconnect from peer.
27142717
assert len(l2.rpc.listpeers(l1.info['id'])['peers']) == 0

0 commit comments

Comments
 (0)