Skip to content

Commit 508ce92

Browse files
wqxoxorustyrussell
authored andcommitted
opening: reject fundchannel_complete with unsigned non-segwit inputs
We can't know the txid of an unsigned PSBT with non-segwit (legacy P2PKH/P2SH) inputs. wally_psbt_extract() fills in an empty scriptSig for unsigned inputs, giving a txid that changes once the user signs and broadcasts. openingd then waits forever for a tx it will never see. Changelog-Fixed: fundchannel_complete: reject PSBTs with unsigned non-segwit inputs (txid is indeterminate until signed).
1 parent 2599c09 commit 508ce92

2 files changed

Lines changed: 45 additions & 0 deletions

File tree

lightningd/opening_control.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,18 @@ static struct command_result *json_fundchannel_complete(struct command *cmd,
10941094
[*funding_txout_num].amount,
10951095
fmt_amount_sat(tmpctx, fc->funding_sats));
10961096

1097+
/* Unsigned non-segwit inputs have malleable txids. */
1098+
for (size_t i = 0; i < funding_psbt->num_inputs; i++) {
1099+
struct wally_psbt_input *in = &funding_psbt->inputs[i];
1100+
if (!in->witness_utxo
1101+
&& !wally_map_get_integer(&in->psbt_fields,
1102+
/* PSBT_IN_FINAL_SCRIPTSIG */ 0x07))
1103+
return command_fail(cmd, FUNDING_PSBT_INVALID,
1104+
"Input %zu is non-segwit and unsigned:"
1105+
" txid is unknown until signed",
1106+
i);
1107+
}
1108+
10971109
funding_txid = tal(cmd, struct bitcoin_txid);
10981110
psbt_txid(NULL, funding_psbt, funding_txid, NULL);
10991111

tests/test_connection.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,6 +1779,39 @@ def test_funding_external_wallet(node_factory, bitcoind):
17791779
l3.rpc.close(l2.info["id"])
17801780

17811781

1782+
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
1783+
@pytest.mark.openchannel('v1')
1784+
def test_fundchannel_complete_rejects_nonsegwit_unsigned(node_factory, bitcoind):
1785+
l1, l2 = node_factory.get_nodes(2)
1786+
l1.connect(l2)
1787+
1788+
amount = 1_000_000
1789+
1790+
start = l1.rpc.fundchannel_start(l2.info['id'], amount)
1791+
funding_addr = start['funding_address']
1792+
1793+
# P2PKH (non-segwit) input.
1794+
legacy_addr = bitcoind.rpc.getnewaddress("", "legacy")
1795+
bitcoind.rpc.sendtoaddress(legacy_addr, 0.05)
1796+
bitcoind.generate_block(1)
1797+
1798+
utxos = bitcoind.rpc.listunspent(1, 9999999, [legacy_addr])
1799+
assert len(utxos) > 0
1800+
utxo = utxos[0]
1801+
1802+
psbt = bitcoind.rpc.walletcreatefundedpsbt(
1803+
[{"txid": utxo['txid'], "vout": utxo['vout']}],
1804+
[{funding_addr: amount / 10**8}],
1805+
0,
1806+
{"add_inputs": False}
1807+
)['psbt']
1808+
1809+
with pytest.raises(RpcError, match=r'non-segwit and unsigned'):
1810+
l1.rpc.fundchannel_complete(l2.info['id'], psbt)
1811+
1812+
l1.rpc.fundchannel_cancel(l2.info['id'])
1813+
1814+
17821815
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
17831816
@pytest.mark.openchannel('v1') # We manually turn on dual-funding for select nodes
17841817
def test_multifunding_v1_v2_mixed(node_factory, bitcoind):

0 commit comments

Comments
 (0)