diff --git a/doc/lightningd-config.5 b/doc/lightningd-config.5 index e52350534..16e1e84df 100644 --- a/doc/lightningd-config.5 +++ b/doc/lightningd-config.5 @@ -281,6 +281,14 @@ extremely busy node for you to even notice\. .SH Lightning channel and HTLC options + \fBlarge-channels\fR +Removes capacity limits for channel creation\. Version 1\.0 of the specification +limited channel sizes to 16777216 satoshi\. With this option (which your +node will advertize to peers), your node will accept larger incoming channels +and if the peer supports it, will open larger channels\. Note: this option +is spelled \fBlarge-channels\fR but it's pronounced \fBwumbo\fR\. + + \fBwatchtime-blocks\fR=\fIBLOCKS\fR How long we need to spot an outdated close attempt: on opening a channel we tell our peer that this is how long they’ll have to wait if they diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 47de08353..f69893735 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -230,6 +230,13 @@ extremely busy node for you to even notice. ### Lightning channel and HTLC options + **large-channels** +Removes capacity limits for channel creation. Version 1.0 of the specification +limited channel sizes to 16777215 satoshi. With this option (which your +node will advertize to peers), your node will accept larger incoming channels +and if the peer supports it, will open larger channels. Note: this option +is spelled **large-channels** but it's pronounced **wumbo**. + **watchtime-blocks**=*BLOCKS* How long we need to spot an outdated close attempt: on opening a channel we tell our peer that this is how long they’ll have to wait if they diff --git a/lightningd/options.c b/lightningd/options.c index 0f65e96f7..9289e6e96 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -742,6 +742,14 @@ static char *opt_start_daemon(struct lightningd *ld) errx(1, "Died with signal %u", WTERMSIG(exitcode)); } +static char *opt_set_wumbo(struct lightningd *ld) +{ + feature_set_or(ld->feature_set, + take(feature_set_for_feature(NULL, + OPTIONAL_FEATURE(OPT_LARGE_CHANNELS)))); + return NULL; +} + static void register_opts(struct lightningd *ld) { /* This happens before plugins started */ @@ -774,6 +782,11 @@ static void register_opts(struct lightningd *ld) &ld->wallet_dsn, "Location of the wallet database."); + /* This affects our features, so set early. */ + opt_register_early_noarg("--large-channels|--wumbo", + opt_set_wumbo, ld, + "Allow channels larger than 0.16777215 BTC"); + opt_register_noarg("--help|-h", opt_lightningd_usage, ld, "Print this message."); opt_register_arg("--rgb", opt_set_rgb, NULL, ld, @@ -1188,6 +1201,10 @@ static void add_config(struct lightningd *ld, ? "false" : "true"); } else if (opt->cb == (void *)opt_set_hsm_password) { json_add_bool(response, "encrypted-hsm", ld->encrypted_hsm); + } else if (opt->cb == (void *)opt_set_wumbo) { + json_add_bool(response, "wumbo", + feature_offered(ld->feature_set->bits[INIT_FEATURE], + OPT_LARGE_CHANNELS)); } else { /* Insert more decodes here! */ assert(!"A noarg option was added but was not handled"); diff --git a/tests/test_connection.py b/tests/test_connection.py index 89a1d08a3..0a5892162 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3,7 +3,7 @@ from decimal import Decimal from fixtures import * # noqa: F401,F403 from fixtures import TEST_NETWORK from flaky import flaky # noqa: F401 -from pyln.client import RpcError +from pyln.client import RpcError, Millisatoshi from utils import ( DEVELOPER, only_one, wait_for, sync_blockheight, VALGRIND, TIMEOUT, SLOW_MACHINE, expected_features @@ -2220,3 +2220,47 @@ def test_pay_disconnect_stress(node_factory, executor): pass fut.result() + + +def test_wumbo_channels(node_factory, bitcoind): + f = bytes.fromhex(expected_features()) + + # OPT_LARGE_CHANNELS = 18 (19 for us). 0x080000 + f = (f[:-3] + bytes([f[-3] | 0x08]) + f[-2:]).hex() + + l1, l2, l3 = node_factory.get_nodes(3, + opts=[{'large-channels': None}, + {'large-channels': None}, + {}]) + conn = l1.rpc.connect(l2.info['id'], 'localhost', port=l2.port) + assert conn['features'] == f + assert only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['features'] == f + + # Now, can we open a giant channel? + l1.fundwallet(1 << 26) + l1.rpc.fundchannel(l2.info['id'], 1 << 24) + + # Get that mined, and announced. + bitcoind.generate_block(6, wait_for_mempool=1) + + # Connect l3, get gossip. + l3.rpc.connect(l1.info['id'], 'localhost', port=l1.port) + wait_for(lambda: len(l3.rpc.listnodes(l1.info['id'])['nodes']) == 1) + wait_for(lambda: 'features' in only_one(l3.rpc.listnodes(l1.info['id'])['nodes'])) + + # Make sure channel capacity is what we expected. + assert ([c['amount_msat'] for c in l3.rpc.listchannels()['channels']] + == [Millisatoshi(str(1 << 24) + "sat")] * 2) + + # Make sure we can't open a wumbo channel if we don't agree. + with pytest.raises(RpcError, match='Amount exceeded'): + l1.rpc.fundchannel(l3.info['id'], 1 << 24) + + # But we can open and announce a normal one. + l1.rpc.fundchannel(l3.info['id'], 'all') + bitcoind.generate_block(6, wait_for_mempool=1) + wait_for(lambda: l1.channel_state(l3) == 'CHANNELD_NORMAL') + + # Make sure l2 sees correct size. + wait_for(lambda: [c['amount_msat'] for c in l2.rpc.listchannels(l1.get_channel_scid(l3))['channels']] + == [Millisatoshi(str((1 << 24) - 1) + "sat")] * 2)