Skip to content

Commit

Permalink
pytest: test various preapprove scenarios.
Browse files Browse the repository at this point in the history
We wire through --dev options into the hsmd, and test preapprove accept and deby
with both old and new protocols.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
  • Loading branch information
rustyrussell committed May 7, 2024
1 parent bd6cf99 commit c7339ea
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 0 deletions.
2 changes: 2 additions & 0 deletions hsmd/hsmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,8 @@ static struct io_plan *preinit_hsm(struct io_conn *conn,
if (tlv->no_preapprove_check)
dev_no_preapprove_check = *tlv->no_preapprove_check;

status_debug("preinit: dev_fail_preapprove = %u, dev_no_preapprove_check = %u",
dev_fail_preapprove, dev_no_preapprove_check);
/* We don't send a reply, just read next */
return client_read_next(conn, c);
}
Expand Down
4 changes: 4 additions & 0 deletions hsmd/libhsmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,8 @@ static u8 *handle_preapprove_invoice(struct hsmd_client *c, const u8 *msg_in)
&& !fromwire_hsmd_preapprove_invoice_check(tmpctx, msg_in, &invstring, &check_only))
return hsmd_status_malformed_request(c, msg_in);

hsmd_status_debug("preapprove_invoice: check_only=%u", check_only);

/* This stub always approves unless overridden */
approved = !dev_fail_preapprove;

Expand All @@ -809,6 +811,8 @@ static u8 *handle_preapprove_keysend(struct hsmd_client *c, const u8 *msg_in)
return hsmd_status_malformed_request(c, msg_in);
}

hsmd_status_debug("preapprove_keysend: check_only=%u", check_only);

/* This stub always approves unless overridden */
approved = !dev_fail_preapprove;

Expand Down
15 changes: 15 additions & 0 deletions lightningd/hsm_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,21 @@ struct ext_key *hsm_init(struct lightningd *ld)
}

ld->hsm_fd = fds[0];

if (ld->developer) {
struct tlv_hsmd_dev_preinit_tlvs *tlv;

tlv = tlv_hsmd_dev_preinit_tlvs_new(tmpctx);
tlv->fail_preapprove = tal_dup(tlv, bool,
&ld->dev_hsmd_fail_preapprove);
tlv->no_preapprove_check = tal_dup(tlv, bool,
&ld->dev_hsmd_no_preapprove_check);

msg = towire_hsmd_dev_preinit(tmpctx, tlv);
if (!wire_sync_write(ld->hsm_fd, msg))
err(EXITCODE_HSM_GENERIC_ERROR, "Writing preinit msg to hsm");
}

if (!wire_sync_write(ld->hsm_fd, towire_hsmd_init(tmpctx,
&chainparams->bip32_key_version,
chainparams,
Expand Down
2 changes: 2 additions & 0 deletions lightningd/lightningd.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
ld->dev_no_ping_timer = false;
ld->dev_any_channel_type = false;
ld->dev_allow_shutdown_destination_change = false;
ld->dev_hsmd_no_preapprove_check = false;
ld->dev_hsmd_fail_preapprove = false;

/*~ This is a CCAN list: an embedded double-linked list. It's not
* really typesafe, but relies on convention to access the contents.
Expand Down
4 changes: 4 additions & 0 deletions lightningd/lightningd.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,10 @@ struct lightningd {
/* Allow changing of shutdown output point even if dangerous */
bool dev_allow_shutdown_destination_change;

/* hsmd characteristic tweaks */
bool dev_hsmd_no_preapprove_check;
bool dev_hsmd_fail_preapprove;

/* tor support */
struct wireaddr *proxyaddr;
bool always_use_proxy;
Expand Down
8 changes: 8 additions & 0 deletions lightningd/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,14 @@ static void dev_register_opts(struct lightningd *ld)
opt_set_bool,
&ld->dev_allow_shutdown_destination_change,
"Allow destination override on close, even if risky");
clnopt_noarg("--dev-hsmd-no-preapprove-check", OPT_DEV,
opt_set_bool,
&ld->dev_hsmd_no_preapprove_check,
"Tell hsmd not to support preapprove_check msgs");
clnopt_noarg("--dev-hsmd-fail-preapprove", OPT_DEV,
opt_set_bool,
&ld->dev_hsmd_fail_preapprove,
"Tell hsmd to always deny preapprove_invoice / preapprove_keysend");
/* This is handled directly in daemon_developer_mode(), so we ignore it here */
clnopt_noarg("--dev-debug-self", OPT_DEV,
opt_ignore,
Expand Down
65 changes: 65 additions & 0 deletions tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4094,3 +4094,68 @@ def test_set_feerate_offset(node_factory, bitcoind):

l1.daemon.wait_for_log(' to CLOSINGD_COMPLETE')
l2.daemon.wait_for_log(' to CLOSINGD_COMPLETE')


@pytest.mark.parametrize("preapprove", [False, True])
def test_preapprove(node_factory, bitcoind, preapprove):
# l1 uses old routine which doesn't support check.
opts = [{'dev-hsmd-no-preapprove-check': None}, {}]
if preapprove is False:
opts[0]['dev-hsmd-fail-preapprove'] = None
opts[1]['dev-hsmd-fail-preapprove'] = None

l1, l2 = node_factory.line_graph(2, opts=opts)

inv = l1.rpc.invoice(123000, 'label', 'description', 3700)['bolt11']
if preapprove:
l2.rpc.check('preapproveinvoice', bolt11=inv)
else:
with pytest.raises(RpcError, match='invoice was declined'):
l2.rpc.check('preapproveinvoice', bolt11=inv)

l2.daemon.wait_for_log("preapprove_invoice: check_only=1")

# But l1 can't check properly, will always pass.
inv = l2.rpc.invoice(123000, 'label', 'description', 3700)['bolt11']
l1.rpc.check('preapproveinvoice', bolt11=inv)

assert not l1.daemon.is_in_log("preapprove_invoice: check_only=1")

# But if we try to actually preapprove we fail if told.
if preapprove:
l1.rpc.preapproveinvoice(inv)
else:
with pytest.raises(RpcError, match='invoice was declined'):
l1.rpc.preapproveinvoice(bolt11=inv)
l1.daemon.wait_for_log("preapprove_invoice: check_only=0")

# Same for keysend
if preapprove:
l2.rpc.check('preapprovekeysend',
destination=l1.info['id'],
payment_hash='00' * 32,
amount_msat=1000)
else:
with pytest.raises(RpcError, match='keysend was declined'):
l2.rpc.check('preapprovekeysend',
destination=l1.info['id'],
payment_hash='00' * 32,
amount_msat=1000)

l2.daemon.wait_for_log("preapprove_keysend: check_only=1")

# But l1 can't check properly, will always pass.
l1.rpc.check('preapprovekeysend',
destination=l2.info['id'],
payment_hash='00' * 32,
amount_msat=1000)

assert not l1.daemon.is_in_log("preapprove_keysend: check_only=1")

# But if we try to actually preapprove we fail if told.
if preapprove:
l1.rpc.preapprovekeysend(l2.info['id'], '00' * 32, 1000)
else:
with pytest.raises(RpcError, match='keysend was declined'):
l1.rpc.preapprovekeysend(l2.info['id'], '00' * 32, 1000)
l1.daemon.wait_for_log("preapprove_keysend: check_only=0")
6 changes: 6 additions & 0 deletions wallet/test/run-wallet.c
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,9 @@ void subkey_from_hmac(const char *prefix UNNEEDED,
const struct secret *base UNNEEDED,
struct secret *key UNNEEDED)
{ fprintf(stderr, "subkey_from_hmac called!\n"); abort(); }
/* Generated stub for tlv_hsmd_dev_preinit_tlvs_new */
struct tlv_hsmd_dev_preinit_tlvs *tlv_hsmd_dev_preinit_tlvs_new(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "tlv_hsmd_dev_preinit_tlvs_new called!\n"); abort(); }
/* Generated stub for to_canonical_invstr */
const char *to_canonical_invstr(const tal_t *ctx UNNEEDED, const char *invstring UNNEEDED)
{ fprintf(stderr, "to_canonical_invstr called!\n"); abort(); }
Expand Down Expand Up @@ -1070,6 +1073,9 @@ u8 *towire_hsmd_cupdate_sig_req(const tal_t *ctx UNNEEDED, const u8 *cu UNNEEDED
/* Generated stub for towire_hsmd_derive_secret */
u8 *towire_hsmd_derive_secret(const tal_t *ctx UNNEEDED, const u8 *info UNNEEDED)
{ fprintf(stderr, "towire_hsmd_derive_secret called!\n"); abort(); }
/* Generated stub for towire_hsmd_dev_preinit */
u8 *towire_hsmd_dev_preinit(const tal_t *ctx UNNEEDED, const struct tlv_hsmd_dev_preinit_tlvs *tlvs UNNEEDED)
{ fprintf(stderr, "towire_hsmd_dev_preinit called!\n"); abort(); }
/* Generated stub for towire_hsmd_forget_channel */
u8 *towire_hsmd_forget_channel(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 dbid UNNEEDED)
{ fprintf(stderr, "towire_hsmd_forget_channel called!\n"); abort(); }
Expand Down

0 comments on commit c7339ea

Please sign in to comment.