From dddfdc7f10af41ae216447419a02d7d452ec74e2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 1 May 2019 10:18:13 +0930 Subject: [PATCH] pytest: add dev hack for testing option_upfront_shutdown_script. Signed-off-by: Rusty Russell --- CHANGELOG.md | 1 + openingd/openingd.c | 16 +++++++++++-- tests/test_closing.py | 53 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad017112a..ed654d85d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - JSON API: `listforwards` now includes the time an HTLC was received and when it was resolved. Both are expressed as UNIX timestamps to facilitate parsing (Issue [#2491](https://github.com/ElementsProject/lightning/issues/2491), PR [#2528](https://github.com/ElementsProject/lightning/pull/2528)) - JSON API: new plugin `invoice_payment` hook for intercepting invoices before they're paid. - plugin: the `connected` hook can now send an `error_message` to the rejected peer. +- Protocol: we now enforce `option_upfront_shutdown_script` if a peer negotiates it. ### Changed diff --git a/openingd/openingd.c b/openingd/openingd.c index 3a30615ca..1bac87d1e 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -110,6 +110,17 @@ struct state { const struct chainparams *chainparams; }; +static const u8 *dev_upfront_shutdown_script(const tal_t *ctx) +{ +#if DEVELOPER + /* This is a hack, for feature testing */ + const char *e = getenv("DEV_OPENINGD_UPFRONT_SHUTDOWN_SCRIPT"); + if (e) + return tal_hexdata(ctx, e, strlen(e)); +#endif + return NULL; +} + /*~ If we can't agree on parameters, we fail to open the channel. If we're * the funder, we need to tell lightningd, otherwise it never really notices. */ static void negotiation_aborted(struct state *state, bool am_funder, @@ -517,7 +528,8 @@ static u8 *funder_channel(struct state *state, &state->our_points.delayed_payment, &state->our_points.htlc, &state->first_per_commitment_point[LOCAL], - channel_flags, NULL); + channel_flags, + dev_upfront_shutdown_script(tmpctx)); sync_crypto_write(&state->cs, PEER_FD, take(msg)); /* This is usually a very transient state... */ @@ -1080,7 +1092,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) &state->our_points.delayed_payment, &state->our_points.htlc, &state->first_per_commitment_point[LOCAL], - NULL); + dev_upfront_shutdown_script(tmpctx)); sync_crypto_write(&state->cs, PEER_FD, take(msg)); diff --git a/tests/test_closing.py b/tests/test_closing.py index f00e66c80..9c150882a 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -1491,3 +1491,56 @@ def test_shutdown(node_factory): raise Exception("Node {} has memory leaks: {}" .format(l1.daemon.lightning_dir, leaks)) l1.rpc.stop() + + +@unittest.skipIf(not DEVELOPER, "needs to set upfront_shutdown_script") +def test_option_upfront_shutdown_script(node_factory, bitcoind): + l1 = node_factory.get_node(start=False) + # Insist on upfront script we're not going to match. + l1.daemon.env["DEV_OPENINGD_UPFRONT_SHUTDOWN_SCRIPT"] = "76a91404b61f7dc1ea0dc99424464cc4064dc564d91e8988ac" + l1.start() + + l2 = node_factory.get_node() + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.fund_channel(l2, 1000000, False) + + l1.rpc.close(l2.info['id']) + + # l2 will close unilaterally when it dislikes shutdown script. + l1.daemon.wait_for_log(r'received ERROR.*scriptpubkey .* is not as agreed upfront \(76a91404b61f7dc1ea0dc99424464cc4064dc564d91e8988ac\)') + + # Clear channel. + wait_for(lambda: len(bitcoind.rpc.getrawmempool()) != 0) + bitcoind.generate_block(1) + wait_for(lambda: [c['state'] for c in only_one(l1.rpc.listpeers()['peers'])['channels']] == ['ONCHAIN']) + + # Works when l2 closes channel, too. + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.fund_channel(l2, 1000000, False) + + l2.rpc.close(l1.info['id']) + + # l2 will close unilaterally when it dislikes shutdown script. + l1.daemon.wait_for_log(r'received ERROR.*scriptpubkey .* is not as agreed upfront \(76a91404b61f7dc1ea0dc99424464cc4064dc564d91e8988ac\)') + + # Clear channel. + wait_for(lambda: len(bitcoind.rpc.getrawmempool()) != 0) + bitcoind.generate_block(1) + wait_for(lambda: [c['state'] for c in only_one(l1.rpc.listpeers()['peers'])['channels']] == ['ONCHAIN', 'ONCHAIN']) + + # Figure out what address it will try to use. + keyidx = int(l1.db_query("SELECT val FROM vars WHERE name='bip32_max_index';")[0]['val']) + + # Expect 1 for change address, 1 for the channel final address. + addr = l1.rpc.call('dev-listaddrs', [keyidx + 2])['addresses'][-1] + + # Now, if we specify upfront and it's OK, all good. + l1.stop() + # We need to prepend the segwit version (0) and push opcode (14). + l1.daemon.env["DEV_OPENINGD_UPFRONT_SHUTDOWN_SCRIPT"] = '0014' + addr['bech32_redeemscript'] + l1.start() + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.rpc.fundchannel(l2.info['id'], 1000000) + l1.rpc.close(l2.info['id']) + wait_for(lambda: sorted([c['state'] for c in only_one(l1.rpc.listpeers()['peers'])['channels']]) == ['CLOSINGD_COMPLETE', 'ONCHAIN', 'ONCHAIN'])