Skip to content

Commit 22fe60f

Browse files
committed
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 53a5512 commit 22fe60f

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
@@ -1084,6 +1084,18 @@ static struct command_result *json_fundchannel_complete(struct command *cmd,
10841084
[*funding_txout_num].amount,
10851085
fmt_amount_sat(tmpctx, fc->funding_sats));
10861086

1087+
/* Unsigned non-segwit inputs have malleable txids. */
1088+
for (size_t i = 0; i < funding_psbt->num_inputs; i++) {
1089+
struct wally_psbt_input *in = &funding_psbt->inputs[i];
1090+
if (!in->witness_utxo
1091+
&& !wally_map_get_integer(&in->psbt_fields,
1092+
/* PSBT_IN_FINAL_SCRIPTSIG */ 0x07))
1093+
return command_fail(cmd, FUNDING_PSBT_INVALID,
1094+
"Input %zu is non-segwit and unsigned:"
1095+
" txid is unknown until signed",
1096+
i);
1097+
}
1098+
10871099
funding_txid = tal(cmd, struct bitcoin_txid);
10881100
psbt_txid(NULL, funding_psbt, funding_txid, NULL);
10891101

tests/test_connection.py

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

17771777

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

0 commit comments

Comments
 (0)