From 6ea95da34228eee8181b40158011725150c9cb1d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 4 Apr 2024 14:06:13 +1030 Subject: [PATCH] hsmd: add variant of preapprove commands to have it check only, not do anything. Apparently VLS actually does something when we preapprove: if caller is just checking we want to tell it not to do that! I put in a flag so we can test both old and new APIs. Signed-off-by: Rusty Russell --- common/hsm_version.h | 1 + hsmd/hsmd.c | 7 ++++ hsmd/hsmd_wire.csv | 22 ++++++++++++ hsmd/libhsmd.c | 40 +++++++++++++++++---- hsmd/libhsmd.h | 2 ++ lightningd/invoice.c | 31 ++++++++++++---- lightningd/test/run-invoice-select-inchan.c | 15 ++++++++ 7 files changed, 106 insertions(+), 12 deletions(-) diff --git a/common/hsm_version.h b/common/hsm_version.h index 44927bd30..c5c3ee9a3 100644 --- a/common/hsm_version.h +++ b/common/hsm_version.h @@ -24,6 +24,7 @@ * v5 with sign_any_cannouncement: 5fdb9068c43a21887dc03f7dce410d2e3eeff6277f0d49b4fc56595a798fd4a4 * v5 drop init v2: 5024454532fe5a78bb7558000cb344190888b9915360d3d56ddca22eaba9b872 * v5 with dev_preinit: b93e18534a468a4aa9f7015db42e9c363c32aeee5f9146b36dc953ebbdc3d33c + * v5 with preapprove_check: 0ed6dd4ea2c02b67c51b1420b3d07ab2227a4c06ce7e2942d946967687e9baf7 */ #define HSM_MIN_VERSION 5 #define HSM_MAX_VERSION 5 diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 6599f777d..7d42c3e74 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -439,6 +439,9 @@ static struct io_plan *preinit_hsm(struct io_conn *conn, if (tlv->fail_preapprove) dev_fail_preapprove = *tlv->fail_preapprove; + if (tlv->no_preapprove_check) + dev_no_preapprove_check = *tlv->no_preapprove_check; + /* We don't send a reply, just read next */ return client_read_next(conn, c); } @@ -688,6 +691,8 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_SIGN_BOLT12: case WIRE_HSMD_PREAPPROVE_INVOICE: case WIRE_HSMD_PREAPPROVE_KEYSEND: + case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK: + case WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK: case WIRE_HSMD_ECDH_REQ: case WIRE_HSMD_CHECK_FUTURE_SECRET: case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY: @@ -741,6 +746,8 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_SIGN_BOLT12_REPLY: case WIRE_HSMD_PREAPPROVE_INVOICE_REPLY: case WIRE_HSMD_PREAPPROVE_KEYSEND_REPLY: + case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK_REPLY: + case WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK_REPLY: case WIRE_HSMD_CHECK_PUBKEY_REPLY: case WIRE_HSMD_SIGN_ANCHORSPEND_REPLY: case WIRE_HSMD_SIGN_HTLC_TX_MINGLE_REPLY: diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index f92a16af1..83bbfbe9a 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -12,6 +12,8 @@ msgtype,hsmd_dev_preinit,99 msgdata,hsmd_dev_preinit,tlvs,hsmd_dev_preinit_tlvs, tlvtype,hsmd_dev_preinit_tlvs,fail_preapprove,1 tlvdata,hsmd_dev_preinit_tlvs,fail_preapprove,fail,bool, +tlvtype,hsmd_dev_preinit_tlvs,no_preapprove_check,3 +tlvdata,hsmd_dev_preinit_tlvs,no_preapprove_check,disable,bool, #include # Start the HSM. @@ -164,6 +166,26 @@ msgdata,hsmd_preapprove_keysend,amount_msat,amount_msat, msgtype,hsmd_preapprove_keysend_reply,139 msgdata,hsmd_preapprove_keysend_reply,approved,bool, +# Preapprove an invoice for payment (with "check_only" option) +msgtype,hsmd_preapprove_invoice_check,51 +msgdata,hsmd_preapprove_invoice_check,invstring,wirestring, +msgdata,hsmd_preapprove_invoice_check,check_only,bool, + +# Result is true if approved, declined if false +msgtype,hsmd_preapprove_invoice_check_reply,151 +msgdata,hsmd_preapprove_invoice_check_reply,approved,bool, + +# Preapprove a keysend payment (with "check_only" option) +msgtype,hsmd_preapprove_keysend_check,52 +msgdata,hsmd_preapprove_keysend_check,destination,node_id, +msgdata,hsmd_preapprove_keysend_check,payment_hash,sha256, +msgdata,hsmd_preapprove_keysend_check,amount_msat,amount_msat, +msgdata,hsmd_preapprove_keysend_check,check_only,bool, + +# Result is true if approved, declined if false +msgtype,hsmd_preapprove_keysend_check_reply,152 +msgdata,hsmd_preapprove_keysend_check_reply,approved,bool, + # Give me ECDH(node-id-secret,point) msgtype,hsmd_ecdh_req,1 msgdata,hsmd_ecdh_req,point,pubkey, diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index 522e67c52..571752b99 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -35,6 +35,7 @@ bool initialized = false; /* Do we fail all preapprove requests? */ bool dev_fail_preapprove = false; +bool dev_no_preapprove_check = false; struct hsmd_client *hsmd_client_new_main(const tal_t *ctx, u64 capabilities, void *extra) @@ -134,6 +135,8 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_SIGN_BOLT12: case WIRE_HSMD_PREAPPROVE_INVOICE: case WIRE_HSMD_PREAPPROVE_KEYSEND: + case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK: + case WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK: case WIRE_HSMD_DERIVE_SECRET: case WIRE_HSMD_CHECK_PUBKEY: case WIRE_HSMD_SIGN_ANY_PENALTY_TO_US: @@ -177,6 +180,8 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_SIGN_BOLT12_REPLY: case WIRE_HSMD_PREAPPROVE_INVOICE_REPLY: case WIRE_HSMD_PREAPPROVE_KEYSEND_REPLY: + case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK_REPLY: + case WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK_REPLY: case WIRE_HSMD_DERIVE_SECRET_REPLY: case WIRE_HSMD_CHECK_PUBKEY_REPLY: case WIRE_HSMD_SIGN_ANCHORSPEND_REPLY: @@ -775,7 +780,10 @@ static u8 *handle_preapprove_invoice(struct hsmd_client *c, const u8 *msg_in) { char *invstring; bool approved; - if (!fromwire_hsmd_preapprove_invoice(tmpctx, msg_in, &invstring)) + bool check_only = false; + + if (!fromwire_hsmd_preapprove_invoice(tmpctx, msg_in, &invstring) + && !fromwire_hsmd_preapprove_invoice_check(tmpctx, msg_in, &invstring, &check_only)) return hsmd_status_malformed_request(c, msg_in); /* This stub always approves unless overridden */ @@ -793,8 +801,13 @@ static u8 *handle_preapprove_keysend(struct hsmd_client *c, const u8 *msg_in) struct sha256 payment_hash; struct amount_msat amount_msat; bool approved; - if (!fromwire_hsmd_preapprove_keysend(msg_in, &destination, &payment_hash, &amount_msat)) + bool check_only = false; + + if (!fromwire_hsmd_preapprove_keysend(msg_in, &destination, &payment_hash, &amount_msat) + && !fromwire_hsmd_preapprove_keysend_check(msg_in, &destination, &payment_hash, + &amount_msat, &check_only)) { return hsmd_status_malformed_request(c, msg_in); + } /* This stub always approves unless overridden */ approved = !dev_fail_preapprove; @@ -2049,8 +2062,10 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, case WIRE_HSMD_SIGN_BOLT12: return handle_sign_bolt12(client, msg); case WIRE_HSMD_PREAPPROVE_INVOICE: + case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK: return handle_preapprove_invoice(client, msg); case WIRE_HSMD_PREAPPROVE_KEYSEND: + case WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK: return handle_preapprove_keysend(client, msg); case WIRE_HSMD_SIGN_MESSAGE: return handle_sign_message(client, msg); @@ -2140,6 +2155,8 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, case WIRE_HSMD_SIGN_BOLT12_REPLY: case WIRE_HSMD_PREAPPROVE_INVOICE_REPLY: case WIRE_HSMD_PREAPPROVE_KEYSEND_REPLY: + case WIRE_HSMD_PREAPPROVE_INVOICE_CHECK_REPLY: + case WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK_REPLY: case WIRE_HSMD_CHECK_PUBKEY_REPLY: case WIRE_HSMD_SIGN_ANCHORSPEND_REPLY: case WIRE_HSMD_SIGN_HTLC_TX_MINGLE_REPLY: @@ -2166,7 +2183,10 @@ u8 *hsmd_init(struct secret hsm_secret, const u64 hsmd_version, WIRE_HSMD_CHECK_OUTPOINT, WIRE_HSMD_FORGET_CHANNEL, WIRE_HSMD_REVOKE_COMMITMENT_TX, + WIRE_HSMD_PREAPPROVE_INVOICE_CHECK, + WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK, }; + const u32 *caps; /*~ Don't swap this. */ sodium_mlock(secretstuff.hsm_secret.data, @@ -2288,6 +2308,17 @@ u8 *hsmd_init(struct secret hsm_secret, const u64 hsmd_version, &secretstuff.hsm_secret, sizeof(secretstuff.hsm_secret), "derived secrets", strlen("derived secrets")); + /* Capabilities arg needs to be a tal array */ + if (dev_no_preapprove_check) { + /* Skip preapprove capabilities */ + caps = tal_dup_arr(tmpctx, u32, + capabilities, ARRAY_SIZE(capabilities) - 2, + 0); + } else { + caps = tal_dup_arr(tmpctx, u32, + capabilities, ARRAY_SIZE(capabilities), 0); + } + /*~ Note: marshalling a bip32 tree only marshals the public side, * not the secrets! So we're not actually handing them out here! * @@ -2295,10 +2326,7 @@ u8 *hsmd_init(struct secret hsm_secret, const u64 hsmd_version, * incompatibility detection) with alternate implementations. */ return take(towire_hsmd_init_reply_v4( - NULL, hsmd_version, - /* Capabilities arg needs to be a tal array */ - tal_dup_arr(tmpctx, u32, capabilities, - ARRAY_SIZE(capabilities), 0), + NULL, hsmd_version, caps, &node_id, &secretstuff.bip32, &bolt12)); } diff --git a/hsmd/libhsmd.h b/hsmd/libhsmd.h index a722926cf..79e9b9e2d 100644 --- a/hsmd/libhsmd.h +++ b/hsmd/libhsmd.h @@ -95,4 +95,6 @@ extern struct privkey *dev_force_privkey; extern struct secret *dev_force_bip32_seed; /* If they specify --dev-hsmd-fail-preapprove it ends up in here. */ extern bool dev_fail_preapprove; +/* If they specify --dev-no-preapprove-check it ends up in here. */ +extern bool dev_no_preapprove_check; #endif /* LIGHTNING_HSMD_LIBHSMD_H */ diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 360ecab04..0b28784d6 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1870,6 +1870,7 @@ static struct command_result *json_preapproveinvoice(struct command *cmd, const char *invstring; struct json_stream *response; bool approved; + u8 *req; const u8 *msg; if (!param(cmd, buffer, params, @@ -1878,11 +1879,20 @@ static struct command_result *json_preapproveinvoice(struct command *cmd, NULL)) return command_param_failed(); - msg = hsm_sync_req(tmpctx, cmd->ld, - take(towire_hsmd_preapprove_invoice(NULL, invstring))); - if (!fromwire_hsmd_preapprove_invoice_reply(msg, &approved)) + /* Old version didn't have `bool check_only` at end */ + if (!hsm_capable(cmd->ld, WIRE_HSMD_PREAPPROVE_INVOICE_CHECK)) + req = towire_hsmd_preapprove_invoice(NULL, invstring); + else + req = towire_hsmd_preapprove_invoice_check(NULL, invstring, false); + + msg = hsm_sync_req(tmpctx, cmd->ld, take(req)); + + /* These are identical, but use separate numbers for clarity */ + if (!fromwire_hsmd_preapprove_invoice_reply(msg, &approved) + && !fromwire_hsmd_preapprove_invoice_check_reply(msg, &approved)) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "HSM gave bad preapprove_invoice_reply %s", tal_hex(msg, msg)); + } if (!approved) return command_fail(cmd, PAY_INVOICE_PREAPPROVAL_DECLINED, "invoice was declined"); @@ -1910,6 +1920,7 @@ static struct command_result *json_preapprovekeysend(struct command *cmd, struct json_stream *response; bool approved; const u8 *msg; + u8 *req; if (!param(cmd, buffer, params, p_req("destination", param_node_id, &destination), @@ -1918,12 +1929,20 @@ static struct command_result *json_preapprovekeysend(struct command *cmd, NULL)) return command_param_failed(); - msg = towire_hsmd_preapprove_keysend(NULL, destination, payment_hash, *amount); + if (!hsm_capable(cmd->ld, WIRE_HSMD_PREAPPROVE_KEYSEND_CHECK)) + req = towire_hsmd_preapprove_keysend(NULL, destination, payment_hash, *amount); + else + req = towire_hsmd_preapprove_keysend_check(NULL, destination, payment_hash, + *amount, false); - msg = hsm_sync_req(tmpctx, cmd->ld, take(msg)); - if (!fromwire_hsmd_preapprove_keysend_reply(msg, &approved)) + msg = hsm_sync_req(tmpctx, cmd->ld, take(req)); + + /* These are identical, but use separate numbers for clarity */ + if (!fromwire_hsmd_preapprove_keysend_reply(msg, &approved) + && !fromwire_hsmd_preapprove_keysend_check_reply(msg, &approved)) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "HSM gave bad preapprove_keysend_reply %s", tal_hex(msg, msg)); + } if (!approved) return command_fail(cmd, PAY_KEYSEND_PREAPPROVAL_DECLINED, "keysend was declined"); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 07c6cdf9e..1df03ec80 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -290,9 +290,15 @@ bool fromwire_connectd_peer_spoke(const tal_t *ctx UNNEEDED, const void *p UNNEE /* Generated stub for fromwire_dualopend_dev_memleak_reply */ bool fromwire_dualopend_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_dualopend_dev_memleak_reply called!\n"); abort(); } +/* Generated stub for fromwire_hsmd_preapprove_invoice_check_reply */ +bool fromwire_hsmd_preapprove_invoice_check_reply(const void *p UNNEEDED, bool *approved UNNEEDED) +{ fprintf(stderr, "fromwire_hsmd_preapprove_invoice_check_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_preapprove_invoice_reply */ bool fromwire_hsmd_preapprove_invoice_reply(const void *p UNNEEDED, bool *approved UNNEEDED) { fprintf(stderr, "fromwire_hsmd_preapprove_invoice_reply called!\n"); abort(); } +/* Generated stub for fromwire_hsmd_preapprove_keysend_check_reply */ +bool fromwire_hsmd_preapprove_keysend_check_reply(const void *p UNNEEDED, bool *approved UNNEEDED) +{ fprintf(stderr, "fromwire_hsmd_preapprove_keysend_check_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_preapprove_keysend_reply */ bool fromwire_hsmd_preapprove_keysend_reply(const void *p UNNEEDED, bool *approved UNNEEDED) { fprintf(stderr, "fromwire_hsmd_preapprove_keysend_reply called!\n"); abort(); } @@ -325,6 +331,9 @@ u32 get_feerate(const struct fee_states *fee_states UNNEEDED, /* Generated stub for hash_htlc_key */ size_t hash_htlc_key(const struct htlc_key *htlc_key UNNEEDED) { fprintf(stderr, "hash_htlc_key called!\n"); abort(); } +/* Generated stub for hsm_capable */ +bool hsm_capable(struct lightningd *ld UNNEEDED, u32 msgtype UNNEEDED) +{ fprintf(stderr, "hsm_capable called!\n"); abort(); } /* Generated stub for hsm_sync_req */ const u8 *hsm_sync_req(const tal_t *ctx UNNEEDED, struct lightningd *ld UNNEEDED, @@ -949,9 +958,15 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, /* Generated stub for towire_hsmd_preapprove_invoice */ u8 *towire_hsmd_preapprove_invoice(const tal_t *ctx UNNEEDED, const wirestring *invstring UNNEEDED) { fprintf(stderr, "towire_hsmd_preapprove_invoice called!\n"); abort(); } +/* Generated stub for towire_hsmd_preapprove_invoice_check */ +u8 *towire_hsmd_preapprove_invoice_check(const tal_t *ctx UNNEEDED, const wirestring *invstring UNNEEDED, bool check_only UNNEEDED) +{ fprintf(stderr, "towire_hsmd_preapprove_invoice_check called!\n"); abort(); } /* Generated stub for towire_hsmd_preapprove_keysend */ u8 *towire_hsmd_preapprove_keysend(const tal_t *ctx UNNEEDED, const struct node_id *destination UNNEEDED, const struct sha256 *payment_hash UNNEEDED, struct amount_msat amount_msat UNNEEDED) { fprintf(stderr, "towire_hsmd_preapprove_keysend called!\n"); abort(); } +/* Generated stub for towire_hsmd_preapprove_keysend_check */ +u8 *towire_hsmd_preapprove_keysend_check(const tal_t *ctx UNNEEDED, const struct node_id *destination UNNEEDED, const struct sha256 *payment_hash UNNEEDED, struct amount_msat amount_msat UNNEEDED, bool check_only UNNEEDED) +{ fprintf(stderr, "towire_hsmd_preapprove_keysend_check called!\n"); abort(); } /* Generated stub for towire_hsmd_sign_bolt12 */ u8 *towire_hsmd_sign_bolt12(const tal_t *ctx UNNEEDED, const wirestring *messagename UNNEEDED, const wirestring *fieldname UNNEEDED, const struct sha256 *merkleroot UNNEEDED, const u8 *publictweak UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_bolt12 called!\n"); abort(); }