mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-17 19:03:42 +01:00
common: support opt_shutdown_anysegwit checks (EXPERIMENTAL_FEATURES).
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
db2198e7b9
commit
d0946b75bc
@ -78,6 +78,10 @@ static const struct feature_style feature_styles[] = {
|
||||
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
|
||||
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT,
|
||||
[CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT } },
|
||||
{ OPT_SHUTDOWN_ANYSEGWIT,
|
||||
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
|
||||
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT,
|
||||
[CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT } },
|
||||
{ OPT_DUAL_FUND,
|
||||
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
|
||||
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT,
|
||||
@ -385,9 +389,12 @@ static const char *feature_name(const tal_t *ctx, size_t f)
|
||||
"option_basic_mpp",
|
||||
"option_support_large_channel",
|
||||
"option_anchor_outputs",
|
||||
"option_anchors_zero_fee_htlc_tx",
|
||||
NULL,
|
||||
"option_shutdown_anysegwit",
|
||||
};
|
||||
|
||||
if (f / 2 >= ARRAY_SIZE(fnames))
|
||||
if (f / 2 >= ARRAY_SIZE(fnames) || !fnames[f / 2])
|
||||
return tal_fmt(ctx, "option_unknown_%zu/%s",
|
||||
COMPULSORY_FEATURE(f), (f & 1) ? "odd" : "even");
|
||||
|
||||
|
@ -113,6 +113,12 @@ u8 *featurebits_or(const tal_t *ctx, const u8 *f1 TAKES, const u8 *f2 TAKES);
|
||||
#define OPT_LARGE_CHANNELS 18
|
||||
#define OPT_ANCHOR_OUTPUTS 20
|
||||
|
||||
/* BOLT-4e329271a358ee52bf43ddbd96776943c5d74508 #9:
|
||||
*
|
||||
* | 26/27 | `option_shutdown_anysegwit` |... IN ...
|
||||
*/
|
||||
#define OPT_SHUTDOWN_ANYSEGWIT 26
|
||||
|
||||
/* BOLT-7b04b1461739c5036add61782d58ac490842d98b #9:
|
||||
* | 222/223 | `option_dual_fund` | ... IN9 ...
|
||||
*/
|
||||
|
@ -1,10 +1,63 @@
|
||||
#include <bitcoin/script.h>
|
||||
#include <common/shutdown_scriptpubkey.h>
|
||||
|
||||
bool valid_shutdown_scriptpubkey(const u8 *scriptpubkey)
|
||||
#include <stdio.h>
|
||||
|
||||
/* BOLT-4e329271a358ee52bf43ddbd96776943c5d74508 #2:
|
||||
* 5. if (and only if) `option_shutdown_anysegwit` is negotiated:
|
||||
* * `OP_1` through `OP_16` inclusive, followed by a single
|
||||
* push of 2 to 40 bytes
|
||||
* (witness program versions 1 through 16)
|
||||
*/
|
||||
static bool is_valid_witnessprog(const u8 *scriptpubkey)
|
||||
{
|
||||
size_t pushlen;
|
||||
|
||||
if (tal_bytelen(scriptpubkey) < 2)
|
||||
return false;
|
||||
|
||||
switch (scriptpubkey[0]) {
|
||||
case OP_1:
|
||||
case OP_2:
|
||||
case OP_3:
|
||||
case OP_4:
|
||||
case OP_5:
|
||||
case OP_6:
|
||||
case OP_7:
|
||||
case OP_8:
|
||||
case OP_9:
|
||||
case OP_10:
|
||||
case OP_11:
|
||||
case OP_12:
|
||||
case OP_13:
|
||||
case OP_14:
|
||||
case OP_15:
|
||||
case OP_16:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "op = %u (invalid)\n", scriptpubkey[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
pushlen = scriptpubkey[1];
|
||||
/* Must be all of the rest of scriptpubkey */
|
||||
if (2 + pushlen != tal_bytelen(scriptpubkey)) {
|
||||
fprintf(stderr, "2 + %zu != %zu\n", pushlen, tal_bytelen(scriptpubkey));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(pushlen >= 2 && pushlen <= 40))
|
||||
fprintf(stderr, "pushlen == %zu\n", pushlen);
|
||||
|
||||
return pushlen >= 2 && pushlen <= 40;
|
||||
}
|
||||
|
||||
bool valid_shutdown_scriptpubkey(const u8 *scriptpubkey,
|
||||
bool anysegwit)
|
||||
{
|
||||
return is_p2pkh(scriptpubkey, NULL)
|
||||
|| is_p2sh(scriptpubkey, NULL)
|
||||
|| is_p2wpkh(scriptpubkey, NULL)
|
||||
|| is_p2wsh(scriptpubkey, NULL);
|
||||
|| is_p2wsh(scriptpubkey, NULL)
|
||||
|| (anysegwit && is_valid_witnessprog(scriptpubkey));
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
* - if the `scriptpubkey` is not in one of the above forms:
|
||||
* - SHOULD fail the connection.
|
||||
*/
|
||||
bool valid_shutdown_scriptpubkey(const u8 *scriptpubkey);
|
||||
bool valid_shutdown_scriptpubkey(const u8 *scriptpubkey,
|
||||
bool anysegwit);
|
||||
|
||||
#endif /* LIGHTNING_COMMON_SHUTDOWN_SCRIPTPUBKEY_H */
|
||||
|
@ -227,6 +227,9 @@ static void peer_got_shutdown(struct channel *channel, const u8 *msg)
|
||||
{
|
||||
u8 *scriptpubkey;
|
||||
struct lightningd *ld = channel->peer->ld;
|
||||
bool anysegwit = feature_negotiated(ld->our_features,
|
||||
channel->peer->their_features,
|
||||
OPT_SHUTDOWN_ANYSEGWIT);
|
||||
|
||||
if (!fromwire_channeld_got_shutdown(channel, msg, &scriptpubkey)) {
|
||||
channel_internal_error(channel, "bad channel_got_shutdown %s",
|
||||
@ -238,7 +241,7 @@ static void peer_got_shutdown(struct channel *channel, const u8 *msg)
|
||||
tal_free(channel->shutdown_scriptpubkey[REMOTE]);
|
||||
channel->shutdown_scriptpubkey[REMOTE] = scriptpubkey;
|
||||
|
||||
if (!valid_shutdown_scriptpubkey(scriptpubkey)) {
|
||||
if (!valid_shutdown_scriptpubkey(scriptpubkey, anysegwit)) {
|
||||
channel_fail_permanent(channel,
|
||||
REASON_PROTOCOL,
|
||||
"Bad shutdown scriptpubkey %s",
|
||||
|
@ -809,6 +809,7 @@ static struct feature_set *default_features(const tal_t *ctx)
|
||||
#if EXPERIMENTAL_FEATURES
|
||||
OPTIONAL_FEATURE(OPT_ANCHOR_OUTPUTS),
|
||||
OPTIONAL_FEATURE(OPT_ONION_MESSAGES),
|
||||
OPTIONAL_FEATURE(OPT_SHUTDOWN_ANYSEGWIT),
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -325,11 +325,15 @@ static bool setup_channel_funder(struct state *state)
|
||||
static void set_remote_upfront_shutdown(struct state *state,
|
||||
u8 *shutdown_scriptpubkey STEALS)
|
||||
{
|
||||
bool anysegwit = feature_negotiated(state->our_features,
|
||||
state->their_features,
|
||||
OPT_SHUTDOWN_ANYSEGWIT);
|
||||
|
||||
state->upfront_shutdown_script[REMOTE]
|
||||
= tal_steal(state, shutdown_scriptpubkey);
|
||||
|
||||
if (shutdown_scriptpubkey
|
||||
&& !valid_shutdown_scriptpubkey(shutdown_scriptpubkey))
|
||||
&& !valid_shutdown_scriptpubkey(shutdown_scriptpubkey, anysegwit))
|
||||
peer_failed_err(state->pps,
|
||||
&state->channel_id,
|
||||
"Unacceptable upfront_shutdown_script %s",
|
||||
|
@ -5,6 +5,7 @@ from shutil import copyfile
|
||||
from utils import (
|
||||
only_one, sync_blockheight, wait_for, DEVELOPER, TIMEOUT,
|
||||
account_balance, first_channel_id, basic_fee, TEST_NETWORK,
|
||||
EXPERIMENTAL_FEATURES,
|
||||
)
|
||||
|
||||
import os
|
||||
@ -2609,3 +2610,80 @@ def test_invalid_upfront_shutdown_script(node_factory, bitcoind, executor):
|
||||
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
|
||||
with pytest.raises(RpcError, match=r'Unacceptable upfront_shutdown_script'):
|
||||
l1.fundchannel(l2, 1000000, False)
|
||||
|
||||
|
||||
@unittest.skipIf(not DEVELOPER, "needs to set upfront_shutdown_script")
|
||||
@pytest.mark.slow_test
|
||||
def test_segwit_shutdown_script(node_factory, bitcoind, executor):
|
||||
"""
|
||||
Try a range of future segwit versions as shutdown scripts. We create many nodes, so this is quite slow under valgrind
|
||||
"""
|
||||
l1 = node_factory.get_node(allow_warning=True)
|
||||
|
||||
# BOLT-4e329271a358ee52bf43ddbd96776943c5d74508 #2:
|
||||
# 5. if (and only if) `option_shutdown_anysegwit` is negotiated:
|
||||
# * `OP_1` through `OP_16` inclusive, followed by a single push of 2 to 40 bytes
|
||||
# (witness program versions 1 through 16)
|
||||
valid = ['51020000', '5128' + '00' * 0x28,
|
||||
'52020000', '5228' + '00' * 0x28,
|
||||
'53020000', '5328' + '00' * 0x28,
|
||||
'54020000', '5428' + '00' * 0x28,
|
||||
'55020000', '5528' + '00' * 0x28,
|
||||
'56020000', '5628' + '00' * 0x28,
|
||||
'57020000', '5728' + '00' * 0x28,
|
||||
'58020000', '5828' + '00' * 0x28,
|
||||
'59020000', '5928' + '00' * 0x28,
|
||||
'5A020000', '5A28' + '00' * 0x28,
|
||||
'5B020000', '5B28' + '00' * 0x28,
|
||||
'5C020000', '5C28' + '00' * 0x28,
|
||||
'5D020000', '5D28' + '00' * 0x28,
|
||||
'5E020000', '5E28' + '00' * 0x28,
|
||||
'5F020000', '5F28' + '00' * 0x28,
|
||||
'60020000', '6028' + '00' * 0x28]
|
||||
invalid = ['50020000', # Not OP_1-OP_16
|
||||
'61020000', # Not OP_1-OP_16
|
||||
'5102000000', # Extra bytes
|
||||
'510100', # Too short
|
||||
'5129' + '00' * 0x29] # Too long
|
||||
|
||||
if EXPERIMENTAL_FEATURES:
|
||||
xsuccess = valid
|
||||
xfail = invalid
|
||||
else:
|
||||
xsuccess = []
|
||||
xfail = valid + invalid
|
||||
|
||||
# More efficient to create them all up-front.
|
||||
nodes = node_factory.get_nodes(len(xfail) + len(xsuccess))
|
||||
|
||||
# Give it one UTXO to spend for each node.
|
||||
addresses = {}
|
||||
for n in nodes:
|
||||
addresses[l1.rpc.newaddr()['bech32']] = (10**6 + 100000) / 10**8
|
||||
bitcoind.rpc.sendmany("", addresses)
|
||||
bitcoind.generate_block(1)
|
||||
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == len(addresses))
|
||||
|
||||
# FIXME: Since we don't support other non-v0 encodings, we need a protocol
|
||||
# test for this (we're actually testing our upfront check, not the real
|
||||
# shutdown one!),
|
||||
for script in xsuccess:
|
||||
# Insist on upfront script we're not going to match.
|
||||
l1.stop()
|
||||
l1.daemon.env["DEV_OPENINGD_UPFRONT_SHUTDOWN_SCRIPT"] = script
|
||||
l1.start()
|
||||
|
||||
l2 = nodes.pop()
|
||||
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
|
||||
l1.rpc.fundchannel(l2.info['id'], 10**6)
|
||||
|
||||
for script in xfail:
|
||||
# Insist on upfront script we're not going to match.
|
||||
l1.stop()
|
||||
l1.daemon.env["DEV_OPENINGD_UPFRONT_SHUTDOWN_SCRIPT"] = script
|
||||
l1.start()
|
||||
|
||||
l2 = nodes.pop()
|
||||
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
|
||||
with pytest.raises(RpcError, match=r'Unacceptable upfront_shutdown_script'):
|
||||
l1.rpc.fundchannel(l2.info['id'], 10**6)
|
||||
|
@ -1895,6 +1895,7 @@ def test_list_features_only(node_factory):
|
||||
]
|
||||
if EXPERIMENTAL_FEATURES:
|
||||
expected += ['option_anchor_outputs/odd']
|
||||
expected += ['option_shutdown_anysegwit/odd']
|
||||
expected += ['option_unknown_102/odd']
|
||||
assert features == expected
|
||||
|
||||
|
@ -26,6 +26,8 @@ def expected_peer_features(wumbo_channels=False, extra=[]):
|
||||
features += [103]
|
||||
# option_anchor_outputs
|
||||
features += [21]
|
||||
# option_shutdown_anysegwit
|
||||
features += [27]
|
||||
if wumbo_channels:
|
||||
features += [19]
|
||||
return hex_bits(features + extra)
|
||||
@ -41,6 +43,8 @@ def expected_node_features(wumbo_channels=False, extra=[]):
|
||||
features += [103]
|
||||
# option_anchor_outputs
|
||||
features += [21]
|
||||
# option_shutdown_anysegwit
|
||||
features += [27]
|
||||
if wumbo_channels:
|
||||
features += [19]
|
||||
return hex_bits(features + extra)
|
||||
|
Loading…
Reference in New Issue
Block a user