diff --git a/lightningd/invoice.c b/lightningd/invoice.c index b2f3ab5c93ff..4cbff80ee2ae 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1954,6 +1954,10 @@ static struct command_result *json_preapproveinvoice(struct command *cmd, return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "HSM gave bad preapprove_invoice_reply %s", tal_hex(msg, msg)); + /* FIXME: hand this to hsm_init so it can do this, for deeper testing! */ + if (cmd->ld->dev_never_preapprove) + approved = false; + if (!approved) return command_fail(cmd, PAY_INVOICE_PREAPPROVAL_DECLINED, "invoice was declined"); @@ -1995,6 +1999,9 @@ static struct command_result *json_preapprovekeysend(struct command *cmd, return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "HSM gave bad preapprove_keysend_reply %s", tal_hex(msg, msg)); + if (cmd->ld->dev_never_preapprove) + approved = false; + if (!approved) return command_fail(cmd, PAY_KEYSEND_PREAPPROVAL_DECLINED, "keysend was declined"); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 6afd5ac28fc4..7552ef688cd1 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -141,6 +141,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->dev_force_tmp_channel_id = NULL; ld->dev_no_htlc_timeout = false; ld->dev_no_version_checks = false; + ld->dev_never_preapprove = false; ld->dev_max_funding_unconfirmed = 2016; ld->dev_ignore_modern_onion = false; ld->dev_disable_commit = -1; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 8f2ddde97602..55ac1869ac8f 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -329,6 +329,7 @@ struct lightningd { bool dev_no_htlc_timeout; bool dev_no_version_checks; + bool dev_never_preapprove; /* Number of blocks we wait for a channel to get funded * if we are the fundee. */ diff --git a/lightningd/options.c b/lightningd/options.c index 5609e66dc6e0..6493b894e94f 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -927,6 +927,10 @@ 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-never-preapprove", OPT_DEV, + opt_set_bool, + &ld->dev_never_preapprove, + "Disallow preapproveinvoice and preapprovekeysend (for testing)"); } static const struct config testnet_config = { diff --git a/tests/test_misc.py b/tests/test_misc.py index 994425ecfd2c..70b770f399b3 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -3995,3 +3995,26 @@ def test_set_feerate_offset(node_factory, bitcoind): l1.daemon.wait_for_log(' to CLOSINGD_COMPLETE') l2.daemon.wait_for_log(' to CLOSINGD_COMPLETE') + + +def test_preapprove(node_factory, bitcoind): + l1, l2 = node_factory.line_graph(2, opts=[{}, {'dev-never-preapprove': None}]) + + # Create some balance, make sure it's entirely settled. + l1.pay(l2, 200000000) + wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['htlcs'] == []) + + # This will fail at the preapprove step. + inv = l1.rpc.invoice(123000, 'label', 'description', 3700)['bolt11'] + with pytest.raises(RpcError, match='invoice was declined'): + l2.rpc.pay(inv) + + # This will fail the same way + with pytest.raises(RpcError, match='invoice was declined'): + l2.rpc.check('pay', bolt11=inv) + + # Now keysend. + with pytest.raises(RpcError, match='keysend was declined'): + l2.rpc.keysend(l1.info['id'], 1000) + with pytest.raises(RpcError, match='keysend was declined'): + l2.rpc.check('keysend', destination=l1.info['id'], amount_msat=1000)