Skip to content

Commit ae2f23d

Browse files
committed
pytest: create warning if we grind signature shorter than 71 bytes, don't fail.
One in 256 times, we will grind a signature to 70 bytes (or shorter). This breaks our feerate tests. Unfortunately the grinding is deterministic, so there doesn't seem to be a way to avoid it. So we add a log message, and then we skip the feerate test if it happens. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1 parent 036fb76 commit ae2f23d

File tree

9 files changed

+59
-12
lines changed

9 files changed

+59
-12
lines changed

hsmd/hsmd.c

+5-2
Original file line numberDiff line numberDiff line change
@@ -442,8 +442,11 @@ static struct io_plan *preinit_hsm(struct io_conn *conn,
442442
if (tlv->no_preapprove_check)
443443
dev_no_preapprove_check = *tlv->no_preapprove_check;
444444

445-
status_debug("preinit: dev_fail_preapprove = %u, dev_no_preapprove_check = %u",
446-
dev_fail_preapprove, dev_no_preapprove_check);
445+
if (tlv->warn_on_overgrind)
446+
dev_warn_on_overgrind = *tlv->warn_on_overgrind;
447+
448+
status_debug("preinit: dev_fail_preapprove = %u, dev_no_preapprove_check = %u, dev_warn_on_overgrind = %u",
449+
dev_fail_preapprove, dev_no_preapprove_check, dev_warn_on_overgrind);
447450
/* We don't send a reply, just read next */
448451
return client_read_next(conn, c);
449452
}

hsmd/hsmd_wire.csv

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ tlvtype,hsmd_dev_preinit_tlvs,fail_preapprove,1
1414
tlvdata,hsmd_dev_preinit_tlvs,fail_preapprove,fail,bool,
1515
tlvtype,hsmd_dev_preinit_tlvs,no_preapprove_check,3
1616
tlvdata,hsmd_dev_preinit_tlvs,no_preapprove_check,disable,bool,
17+
tlvtype,hsmd_dev_preinit_tlvs,warn_on_overgrind,5
18+
tlvdata,hsmd_dev_preinit_tlvs,warn_on_overgrind,enable,bool,
1719

1820
#include <bitcoin/chainparams.h>
1921
# Start the HSM.

hsmd/libhsmd.c

+14
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ bool initialized = false;
4040
/* Do we fail all preapprove requests? */
4141
bool dev_fail_preapprove = false;
4242
bool dev_no_preapprove_check = false;
43+
bool dev_warn_on_overgrind = false;
4344

4445
struct hsmd_client *hsmd_client_new_main(const tal_t *ctx, u64 capabilities,
4546
void *extra)
@@ -553,6 +554,7 @@ static void sign_our_inputs(struct utxo **utxos, struct wally_psbt *psbt)
553554
for (size_t j = 0; j < psbt->num_inputs; j++) {
554555
struct privkey privkey;
555556
struct pubkey pubkey;
557+
bool needed_sig;
556558

557559
if (!wally_psbt_input_spends(&psbt->inputs[j],
558560
&utxo->outpoint))
@@ -590,6 +592,10 @@ static void sign_our_inputs(struct utxo **utxos, struct wally_psbt *psbt)
590592
wally_psbt_signing_cache_enable(psbt, 0);
591593
is_cache_enabled = true;
592594
}
595+
596+
/* We watch for pre-taproot variable-length sigs */
597+
needed_sig = (psbt->inputs[j].signatures.num_items == 0);
598+
593599
if (wally_psbt_sign(psbt, privkey.secret.data,
594600
sizeof(privkey.secret.data),
595601
EC_FLAG_GRIND_R) != WALLY_OK) {
@@ -602,6 +608,14 @@ static void sign_our_inputs(struct utxo **utxos, struct wally_psbt *psbt)
602608
j, fmt_pubkey(tmpctx, &pubkey),
603609
fmt_wally_psbt(tmpctx, psbt));
604610
}
611+
if (dev_warn_on_overgrind
612+
&& needed_sig
613+
&& psbt->inputs[j].signatures.num_items == 1
614+
&& psbt->inputs[j].signatures.items[0].value_len < 71) {
615+
hsmd_status_fmt(LOG_BROKEN, NULL,
616+
"overgrind: short signature length %zu",
617+
psbt->inputs[j].signatures.items[0].value_len);
618+
}
605619
tal_wally_end(psbt);
606620
}
607621
}

hsmd/libhsmd.h

+2
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,6 @@ extern struct secret *dev_force_bip32_seed;
100100
extern bool dev_fail_preapprove;
101101
/* If they specify --dev-no-preapprove-check it ends up in here. */
102102
extern bool dev_no_preapprove_check;
103+
/* If they specify --dev-warn-on-overgrind it ends up in here. */
104+
extern bool dev_warn_on_overgrind;
103105
#endif /* LIGHTNING_HSMD_LIBHSMD_H */

lightningd/hsm_control.c

+2
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ struct ext_key *hsm_init(struct lightningd *ld)
119119
&ld->dev_hsmd_fail_preapprove);
120120
tlv->no_preapprove_check = tal_dup(tlv, bool,
121121
&ld->dev_hsmd_no_preapprove_check);
122+
tlv->warn_on_overgrind = tal_dup(tlv, bool,
123+
&ld->dev_hsmd_warn_on_overgrind);
122124

123125
msg = towire_hsmd_dev_preinit(tmpctx, tlv);
124126
if (!wire_sync_write(ld->hsm_fd, msg))

lightningd/lightningd.c

+1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
151151
ld->dev_allow_shutdown_destination_change = false;
152152
ld->dev_hsmd_no_preapprove_check = false;
153153
ld->dev_hsmd_fail_preapprove = false;
154+
ld->dev_hsmd_warn_on_overgrind = false;
154155
ld->dev_handshake_no_reply = false;
155156
ld->dev_strict_forwarding = false;
156157
ld->dev_limit_connections_inflight = false;

lightningd/lightningd.h

+1
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ struct lightningd {
353353
/* hsmd characteristic tweaks */
354354
bool dev_hsmd_no_preapprove_check;
355355
bool dev_hsmd_fail_preapprove;
356+
bool dev_hsmd_warn_on_overgrind;
356357

357358
/* Tell connectd not to talk after handshake */
358359
bool dev_handshake_no_reply;

lightningd/options.c

+4
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,10 @@ static void dev_register_opts(struct lightningd *ld)
936936
opt_set_crash_timeout, NULL,
937937
ld,
938938
"Crash if we are still going after this long.");
939+
clnopt_noarg("--dev-warn-on-overgrind", OPT_DEV,
940+
opt_set_bool,
941+
&ld->dev_hsmd_warn_on_overgrind,
942+
"Warn if we create signatures that are not exactly 71 bytes.");
939943
/* This is handled directly in daemon_developer_mode(), so we ignore it here */
940944
clnopt_noarg("--dev-debug-self", OPT_DEV,
941945
opt_ignore,

tests/test_wallet.py

+28-10
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,18 @@ def feerate_from_psbt(bitcoind, node, psbt):
301301
return fee / weight * 1000
302302

303303

304+
# I wish we could force libwally to use different entropy and thus force it to
305+
# create 71-byte sigs always!
306+
def did_short_sig(node):
307+
# This can take a moment to appear in the log!
308+
time.sleep(1)
309+
return node.daemon.is_in_log('overgrind: short signature length')
310+
311+
304312
def test_txprepare(node_factory, bitcoind, chainparams):
305313
amount = 1000000
306-
l1 = node_factory.get_node(random_hsm=True)
314+
l1 = node_factory.get_node(random_hsm=True, options={'dev-warn-on-overgrind': None},
315+
broken_log='overgrind: short signature length')
307316
addr = chainparams['example_addr']
308317

309318
# Add some funds to withdraw later
@@ -324,7 +333,8 @@ def test_txprepare(node_factory, bitcoind, chainparams):
324333
assert len(decode['vin']) == 4
325334
assert len(decode['vout']) == 2 if not chainparams['feeoutput'] else 3
326335
# Feerate should be ~ as we asked for
327-
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep['psbt']) < normal_feerate_perkw + 1
336+
if not did_short_sig(l1):
337+
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep['psbt']) < normal_feerate_perkw + 1
328338

329339
# One output will be correct.
330340
outnum = [i for i, o in enumerate(decode['vout']) if o['value'] == Decimal(amount * 3) / 10**8][0]
@@ -353,7 +363,8 @@ def test_txprepare(node_factory, bitcoind, chainparams):
353363
assert decode['vout'][0]['scriptPubKey']['type'] == 'witness_v0_keyhash'
354364
assert scriptpubkey_addr(decode['vout'][0]['scriptPubKey']) == addr
355365
# Feerate should be ~ as we asked for
356-
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep2['psbt']) < normal_feerate_perkw + 1
366+
if not did_short_sig(l1):
367+
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep2['psbt']) < normal_feerate_perkw + 1
357368

358369
# If I cancel the first one, I can get those first 4 outputs.
359370
discard = l1.rpc.txdiscard(prep['txid'])
@@ -373,7 +384,8 @@ def test_txprepare(node_factory, bitcoind, chainparams):
373384
assert decode['vout'][0]['scriptPubKey']['type'] == 'witness_v0_keyhash'
374385
assert scriptpubkey_addr(decode['vout'][0]['scriptPubKey']) == addr
375386
# Feerate should be ~ as we asked for
376-
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep3['psbt']) < normal_feerate_perkw + 1
387+
if not did_short_sig(l1):
388+
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep3['psbt']) < normal_feerate_perkw + 1
377389

378390
# Cannot discard twice.
379391
with pytest.raises(RpcError, match=r'not an unreleased txid'):
@@ -395,7 +407,8 @@ def test_txprepare(node_factory, bitcoind, chainparams):
395407
assert decode['vout'][0]['scriptPubKey']['type'] == 'witness_v0_keyhash'
396408
assert scriptpubkey_addr(decode['vout'][0]['scriptPubKey']) == addr
397409
# Feerate should be ~ as we asked for
398-
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep4['psbt']) < normal_feerate_perkw + 1
410+
if not did_short_sig(l1):
411+
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep4['psbt']) < normal_feerate_perkw + 1
399412
l1.rpc.txdiscard(prep4['txid'])
400413

401414
# Try passing in a utxo set
@@ -404,7 +417,8 @@ def test_txprepare(node_factory, bitcoind, chainparams):
404417
prep5 = l1.rpc.txprepare([{addr:
405418
Millisatoshi(amount * 3.5 * 1000)}], utxos=utxos)
406419
# Feerate should be ~ as we asked for
407-
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep3['psbt']) < normal_feerate_perkw + 1
420+
if not did_short_sig(l1):
421+
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep3['psbt']) < normal_feerate_perkw + 1
408422

409423
# Try passing unconfirmed utxos
410424
unconfirmed_utxo = l1.rpc.withdraw(l1.rpc.newaddr()["bech32"], 10**5)
@@ -445,15 +459,17 @@ def test_txprepare(node_factory, bitcoind, chainparams):
445459
prep5 = l1.rpc.txprepare([{addr: Millisatoshi(amount * 3 * 1000)},
446460
{addr: 'all'}])
447461
# Feerate should be ~ as we asked for
448-
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep5['psbt']) < normal_feerate_perkw + 1
462+
if not did_short_sig(l1):
463+
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep5['psbt']) < normal_feerate_perkw + 1
449464
l1.rpc.txdiscard(prep5['txid'])
450465
with pytest.raises(RpcError, match=r"'all'"):
451466
prep5 = l1.rpc.txprepare([{addr: 'all'}, {addr: 'all'}])
452467

453468
prep5 = l1.rpc.txprepare([{addr: Millisatoshi(amount * 3 * 500 + 100000)},
454469
{addr: Millisatoshi(amount * 3 * 500 - 100000)}])
455470
# Feerate should be ~ as we asked for
456-
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep5['psbt']) < normal_feerate_perkw + 1
471+
if not did_short_sig(l1):
472+
assert normal_feerate_perkw - 1 < feerate_from_psbt(bitcoind, l1, prep5['psbt']) < normal_feerate_perkw + 1
457473
decode = bitcoind.rpc.decoderawtransaction(prep5['unsigned_tx'])
458474
assert decode['txid'] == prep5['txid']
459475
# 4 inputs, 3 outputs(include change).
@@ -485,7 +501,8 @@ def test_txprepare(node_factory, bitcoind, chainparams):
485501

486502
def test_txprepare_feerate(node_factory, bitcoind):
487503
# Make sure it works at different feerates!
488-
l1, l2 = node_factory.get_nodes(2)
504+
l1, l2 = node_factory.get_nodes(2, opts={'dev-warn-on-overgrind': None,
505+
'broken_log': 'overgrind: short signature length'})
489506

490507
# Add some funds to withdraw later
491508
for i in range(20):
@@ -499,7 +516,8 @@ def test_txprepare_feerate(node_factory, bitcoind):
499516
for addrtype in ('bech32', 'p2tr'):
500517
for feerate in range(255, 10000, 250):
501518
prep = l1.rpc.txprepare([{out_addrs[addrtype]: Millisatoshi(9000)}], f"{feerate}perkw")
502-
assert feerate - 1 < feerate_from_psbt(bitcoind, l1, prep['psbt']) < feerate + 1
519+
if not did_short_sig(l1):
520+
assert feerate - 1 < feerate_from_psbt(bitcoind, l1, prep['psbt']) < feerate + 1
503521
l1.rpc.txdiscard(prep6['txid'])
504522

505523

0 commit comments

Comments
 (0)