Merge pull request #4567 from calvinrzachman/max-wumbo
add new max channel size config option
This commit is contained in:
commit
fa342a1230
36
config.go
36
config.go
@ -248,6 +248,7 @@ type Config struct {
|
||||
Alias string `long:"alias" description:"The node alias. Used as a moniker by peers and intelligence services"`
|
||||
Color string `long:"color" description:"The color of the node in hex format (i.e. '#3399FF'). Used to customize node appearance in intelligence services"`
|
||||
MinChanSize int64 `long:"minchansize" description:"The smallest channel size (in satoshis) that we should accept. Incoming channels smaller than this will be rejected"`
|
||||
MaxChanSize int64 `long:"maxchansize" description:"The largest channel size (in satoshis) that we should accept. Incoming channels larger than this will be rejected"`
|
||||
|
||||
DefaultRemoteMaxHtlcs uint16 `long:"default-remote-max-htlcs" description:"The default max_htlc applied when opening or accepting channels. This value limits the number of concurrent HTLCs that the remote party can add to the commitment. The maximum possible value is 483."`
|
||||
|
||||
@ -397,6 +398,7 @@ func DefaultConfig() Config {
|
||||
Alias: defaultAlias,
|
||||
Color: defaultColor,
|
||||
MinChanSize: int64(minChanFundingSize),
|
||||
MaxChanSize: int64(0),
|
||||
DefaultRemoteMaxHtlcs: defaultRemoteMaxHtlcs,
|
||||
NumGraphSyncPeers: defaultMinPeers,
|
||||
HistoricalSyncInterval: discovery.DefaultHistoricalSyncInterval,
|
||||
@ -624,7 +626,7 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) {
|
||||
}
|
||||
|
||||
// Ensure that the specified values for the min and max channel size
|
||||
// don't are within the bounds of the normal chan size constraints.
|
||||
// are within the bounds of the normal chan size constraints.
|
||||
if cfg.Autopilot.MinChannelSize < int64(minChanFundingSize) {
|
||||
cfg.Autopilot.MinChannelSize = int64(minChanFundingSize)
|
||||
}
|
||||
@ -636,6 +638,38 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure that --maxchansize is properly handled when set by user.
|
||||
// For non-Wumbo channels this limit remains 16777215 satoshis by default
|
||||
// as specified in BOLT-02. For wumbo channels this limit is 1,000,000,000.
|
||||
// satoshis (10 BTC). Always enforce --maxchansize explicitly set by user.
|
||||
// If unset (marked by 0 value), then enforce proper default.
|
||||
if cfg.MaxChanSize == 0 {
|
||||
if cfg.ProtocolOptions.Wumbo() {
|
||||
cfg.MaxChanSize = int64(MaxBtcFundingAmountWumbo)
|
||||
} else {
|
||||
cfg.MaxChanSize = int64(MaxBtcFundingAmount)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the user specified values for the min and max channel
|
||||
// size make sense.
|
||||
if cfg.MaxChanSize < cfg.MinChanSize {
|
||||
return nil, fmt.Errorf("invalid channel size parameters: "+
|
||||
"max channel size %v, must be no less than min chan size %v",
|
||||
cfg.MaxChanSize, cfg.MinChanSize,
|
||||
)
|
||||
}
|
||||
|
||||
// Don't allow superflous --maxchansize greater than
|
||||
// BOLT 02 soft-limit for non-wumbo channel
|
||||
if !cfg.ProtocolOptions.Wumbo() && cfg.MaxChanSize > int64(MaxFundingAmount) {
|
||||
return nil, fmt.Errorf("invalid channel size parameters: "+
|
||||
"maximum channel size %v is greater than maximum non-wumbo"+
|
||||
" channel size %v",
|
||||
cfg.MaxChanSize, MaxFundingAmount,
|
||||
)
|
||||
}
|
||||
|
||||
// Ensure a valid max channel fee allocation was set.
|
||||
if cfg.MaxChannelFeeAllocation <= 0 || cfg.MaxChannelFeeAllocation > 1 {
|
||||
return nil, fmt.Errorf("invalid max channel fee allocation: "+
|
||||
|
@ -67,6 +67,11 @@ const (
|
||||
// in the real world.
|
||||
MaxBtcFundingAmount = btcutil.Amount(1<<24) - 1
|
||||
|
||||
// MaxBtcFundingAmountWumbo is a soft-limit on the maximum size of wumbo
|
||||
// channels. This limit is 10 BTC and is the only thing standing between
|
||||
// you and limitless channel size (apart from 21 million cap)
|
||||
MaxBtcFundingAmountWumbo = btcutil.Amount(1000000000)
|
||||
|
||||
// maxLtcFundingAmount is a soft-limit of the maximum channel size
|
||||
// currently accepted on the Litecoin chain within the Lightning
|
||||
// Protocol.
|
||||
@ -360,6 +365,11 @@ type fundingConfig struct {
|
||||
// due to fees.
|
||||
MinChanSize btcutil.Amount
|
||||
|
||||
// MaxChanSize is the largest channel size that we'll accept as an
|
||||
// inbound channel. We have such a parameter, so that you may decide how
|
||||
// WUMBO you would like your channel.
|
||||
MaxChanSize btcutil.Amount
|
||||
|
||||
// MaxPendingChannels is the maximum number of pending channels we
|
||||
// allow for each peer.
|
||||
MaxPendingChannels int
|
||||
@ -1269,13 +1279,11 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
|
||||
return
|
||||
}
|
||||
|
||||
// We'll reject any request to create a channel that's above the
|
||||
// current soft-limit for channel size, but only if we're rejecting all
|
||||
// wumbo channel initiations.
|
||||
if f.cfg.NoWumboChans && msg.FundingAmount > MaxFundingAmount {
|
||||
// Ensure that the remote party respects our maximum channel size.
|
||||
if amt > f.cfg.MaxChanSize {
|
||||
f.failFundingFlow(
|
||||
fmsg.peer, fmsg.msg.PendingChannelID,
|
||||
lnwire.ErrChanTooLarge,
|
||||
lnwallet.ErrChanTooLarge(amt, f.cfg.MaxChanSize),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
@ -432,6 +432,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
|
||||
},
|
||||
ZombieSweeperInterval: 1 * time.Hour,
|
||||
ReservationTimeout: 1 * time.Nanosecond,
|
||||
MaxChanSize: MaxFundingAmount,
|
||||
MaxPendingChannels: lncfg.DefaultMaxPendingChannels,
|
||||
NotifyOpenChannelEvent: evt.NotifyOpenChannelEvent,
|
||||
OpenChannelPredicate: chainedAcceptor,
|
||||
@ -3212,6 +3213,75 @@ func expectOpenChannelMsg(t *testing.T, msgChan chan lnwire.Message) *lnwire.Ope
|
||||
return openChannelReq
|
||||
}
|
||||
|
||||
func TestMaxChannelSizeConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a set of funding managers that will reject wumbo
|
||||
// channels but set --maxchansize explicitly lower than soft-limit.
|
||||
// Verify that wumbo rejecting funding managers will respect --maxchansize
|
||||
// below 16777215 satoshi (MaxFundingAmount) limit.
|
||||
alice, bob := setupFundingManagers(t, func(cfg *fundingConfig) {
|
||||
cfg.NoWumboChans = true
|
||||
cfg.MaxChanSize = MaxFundingAmount - 1
|
||||
})
|
||||
|
||||
// Attempt to create a channel above the limit
|
||||
// imposed by --maxchansize, which should be rejected.
|
||||
updateChan := make(chan *lnrpc.OpenStatusUpdate)
|
||||
errChan := make(chan error, 1)
|
||||
initReq := &openChanReq{
|
||||
targetPubkey: bob.privKey.PubKey(),
|
||||
chainHash: *fundingNetParams.GenesisHash,
|
||||
localFundingAmt: MaxFundingAmount,
|
||||
pushAmt: lnwire.NewMSatFromSatoshis(0),
|
||||
private: false,
|
||||
updates: updateChan,
|
||||
err: errChan,
|
||||
}
|
||||
|
||||
// After processing the funding open message, bob should respond with
|
||||
// an error rejecting the channel that exceeds size limit.
|
||||
alice.fundingMgr.initFundingWorkflow(bob, initReq)
|
||||
openChanMsg := expectOpenChannelMsg(t, alice.msgChan)
|
||||
bob.fundingMgr.processFundingOpen(openChanMsg, alice)
|
||||
assertErrorSent(t, bob.msgChan)
|
||||
|
||||
// Create a set of funding managers that will reject wumbo
|
||||
// channels but set --maxchansize explicitly higher than soft-limit
|
||||
// A --maxchansize greater than this limit should have no effect.
|
||||
tearDownFundingManagers(t, alice, bob)
|
||||
alice, bob = setupFundingManagers(t, func(cfg *fundingConfig) {
|
||||
cfg.NoWumboChans = true
|
||||
cfg.MaxChanSize = MaxFundingAmount + 1
|
||||
})
|
||||
|
||||
// We expect Bob to respond with an Accept channel message.
|
||||
alice.fundingMgr.initFundingWorkflow(bob, initReq)
|
||||
openChanMsg = expectOpenChannelMsg(t, alice.msgChan)
|
||||
bob.fundingMgr.processFundingOpen(openChanMsg, alice)
|
||||
assertFundingMsgSent(t, bob.msgChan, "AcceptChannel")
|
||||
|
||||
// Verify that wumbo accepting funding managers will respect --maxchansize
|
||||
// Create the funding managers, this time allowing
|
||||
// wumbo channels but setting --maxchansize explicitly.
|
||||
tearDownFundingManagers(t, alice, bob)
|
||||
alice, bob = setupFundingManagers(t, func(cfg *fundingConfig) {
|
||||
cfg.NoWumboChans = false
|
||||
cfg.MaxChanSize = btcutil.Amount(100000000)
|
||||
})
|
||||
|
||||
// Attempt to create a channel above the limit
|
||||
// imposed by --maxchansize, which should be rejected.
|
||||
initReq.localFundingAmt = btcutil.SatoshiPerBitcoin + 1
|
||||
|
||||
// After processing the funding open message, bob should respond with
|
||||
// an error rejecting the channel that exceeds size limit.
|
||||
alice.fundingMgr.initFundingWorkflow(bob, initReq)
|
||||
openChanMsg = expectOpenChannelMsg(t, alice.msgChan)
|
||||
bob.fundingMgr.processFundingOpen(openChanMsg, alice)
|
||||
assertErrorSent(t, bob.msgChan)
|
||||
}
|
||||
|
||||
// TestWumboChannelConfig tests that the funding manager will respect the wumbo
|
||||
// channel config param when creating or accepting new channels.
|
||||
func TestWumboChannelConfig(t *testing.T) {
|
||||
@ -3260,6 +3330,7 @@ func TestWumboChannelConfig(t *testing.T) {
|
||||
tearDownFundingManagers(t, alice, bob)
|
||||
alice, bob = setupFundingManagers(t, func(cfg *fundingConfig) {
|
||||
cfg.NoWumboChans = false
|
||||
cfg.MaxChanSize = MaxBtcFundingAmountWumbo
|
||||
})
|
||||
|
||||
// We should now be able to initiate a wumbo channel funding w/o any
|
||||
|
120
lntest/itest/lnd_max_channel_size_test.go
Normal file
120
lntest/itest/lnd_max_channel_size_test.go
Normal file
@ -0,0 +1,120 @@
|
||||
// +build rpctest
|
||||
|
||||
package itest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd"
|
||||
"github.com/lightningnetwork/lnd/lntest"
|
||||
)
|
||||
|
||||
// testMaxChannelSize tests that lnd handles --maxchansize parameter
|
||||
// correctly. Wumbo nodes should enforce a default soft limit of 10 BTC by
|
||||
// default. This limit can be adjusted with --maxchansize config option
|
||||
func testMaxChannelSize(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// We'll make two new nodes, both wumbo but with the default
|
||||
// limit on maximum channel size (10 BTC)
|
||||
wumboNode, err := net.NewNode(
|
||||
"wumbo", []string{"--protocol.wumbo-channels"},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create new node: %v", err)
|
||||
}
|
||||
defer shutdownAndAssert(net, t, wumboNode)
|
||||
|
||||
wumboNode2, err := net.NewNode(
|
||||
"wumbo2", []string{"--protocol.wumbo-channels"},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create new node: %v", err)
|
||||
}
|
||||
defer shutdownAndAssert(net, t, wumboNode2)
|
||||
|
||||
// We'll send 11 BTC to the wumbo node so it can test the wumbo soft limit.
|
||||
ctxb := context.Background()
|
||||
err = net.SendCoins(ctxb, 11*btcutil.SatoshiPerBitcoin, wumboNode)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send coins to wumbo node: %v", err)
|
||||
}
|
||||
|
||||
// Next we'll connect both nodes, then attempt to make a wumbo channel
|
||||
// funding request, which should fail as it exceeds the default wumbo
|
||||
// soft limit of 10 BTC.
|
||||
err = net.EnsureConnected(ctxb, wumboNode, wumboNode2)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to connect peers: %v", err)
|
||||
}
|
||||
|
||||
chanAmt := lnd.MaxBtcFundingAmountWumbo + 1
|
||||
_, err = net.OpenChannel(
|
||||
ctxb, wumboNode, wumboNode2, lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
if err == nil {
|
||||
t.Fatalf("expected channel funding to fail as it exceeds 10 BTC limit")
|
||||
}
|
||||
|
||||
// The test should show failure due to the channel exceeding our max size.
|
||||
if !strings.Contains(err.Error(), "exceeds maximum chan size") {
|
||||
t.Fatalf("channel should be rejected due to size, instead "+
|
||||
"error was: %v", err)
|
||||
}
|
||||
|
||||
// Next we'll create a non-wumbo node to verify that it enforces the
|
||||
// BOLT-02 channel size limit and rejects our funding request.
|
||||
miniNode, err := net.NewNode("mini", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create new node: %v", err)
|
||||
}
|
||||
defer shutdownAndAssert(net, t, miniNode)
|
||||
|
||||
err = net.EnsureConnected(ctxb, wumboNode, miniNode)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to connect peers: %v", err)
|
||||
}
|
||||
|
||||
_, err = net.OpenChannel(
|
||||
ctxb, wumboNode, miniNode, lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
if err == nil {
|
||||
t.Fatalf("expected channel funding to fail as it exceeds 0.16 BTC limit")
|
||||
}
|
||||
|
||||
// The test should show failure due to the channel exceeding our max size.
|
||||
if !strings.Contains(err.Error(), "exceeds maximum chan size") {
|
||||
t.Fatalf("channel should be rejected due to size, instead "+
|
||||
"error was: %v", err)
|
||||
}
|
||||
|
||||
// We'll now make another wumbo node with appropriate maximum channel size
|
||||
// to accept our wumbo channel funding.
|
||||
wumboNode3, err := net.NewNode(
|
||||
"wumbo3", []string{"--protocol.wumbo-channels",
|
||||
fmt.Sprintf("--maxchansize=%v", int64(lnd.MaxBtcFundingAmountWumbo+1))},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create new node: %v", err)
|
||||
}
|
||||
defer shutdownAndAssert(net, t, wumboNode3)
|
||||
|
||||
// Creating a wumbo channel between these two nodes should succeed.
|
||||
err = net.EnsureConnected(ctxb, wumboNode, wumboNode3)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to connect peers: %v", err)
|
||||
}
|
||||
chanPoint := openChannelAndAssert(
|
||||
ctxb, t, net, wumboNode, wumboNode3,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
closeChannelAndAssert(ctxb, t, net, wumboNode, chanPoint, false)
|
||||
|
||||
}
|
@ -14374,6 +14374,10 @@ var testsCases = []*testCase{
|
||||
name: "wumbo channels",
|
||||
test: testWumboChannels,
|
||||
},
|
||||
{
|
||||
name: "maximum channel size",
|
||||
test: testMaxChannelSize,
|
||||
},
|
||||
}
|
||||
|
||||
// TestLightningNetworkDaemon performs a series of integration tests amongst a
|
||||
|
@ -62,7 +62,7 @@ func testWumboChannels(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
|
||||
// The test should indicate a failure due to the channel being too
|
||||
// large.
|
||||
if !strings.Contains(err.Error(), "channel too large") {
|
||||
if !strings.Contains(err.Error(), "exceeds maximum chan size") {
|
||||
t.Fatalf("channel should be rejected due to size, instead "+
|
||||
"error was: %v", err)
|
||||
}
|
||||
|
@ -203,6 +203,15 @@
|
||||
<time> [ERR] FNDG: received funding error from <hex>: chan_id=<hex>, err=channel too large
|
||||
<time> [ERR] RPCS: [/lnrpc.Lightning/OpenChannel]: received funding error from <hex>: chan_id=<hex>, err=channel too large
|
||||
<time> [ERR] RPCS: unable to open channel to NodeKey(<hex>): received funding error from <hex>: chan_id=<hex>, err=channel too large
|
||||
<time> [ERR] FNDG: received funding error from <hex>: chan_id=<hex>, err=chan size of 0.16777216 BTC exceeds maximum chan size of 0.16777215 BTC
|
||||
<time> [ERR] RPCS: [/lnrpc.Lightning/OpenChannel]: received funding error from <hex>: chan_id=<hex>, err=chan size of 0.16777216 BTC exceeds maximum chan size of 0.16777215 BTC
|
||||
<time> [ERR] RPCS: unable to open channel to NodeKey(<hex>): received funding error from <hex>: chan_id=<hex>, err=chan size of 0.16777216 BTC exceeds maximum chan size of 0.16777215 BTC
|
||||
<time> [ERR] FNDG: received funding error from <hex>: chan_id=<hex>, err=chan size of 10.00000001 BTC exceeds maximum chan size of 10 BTC
|
||||
<time> [ERR] RPCS: [/lnrpc.Lightning/OpenChannel]: received funding error from <hex>: chan_id=<hex>, err=chan size of 10.00000001 BTC exceeds maximum chan size of 10 BTC
|
||||
<time> [ERR] RPCS: unable to open channel to NodeKey(<hex>): received funding error from <hex>: chan_id=<hex>, err=chan size of 10.00000001 BTC exceeds maximum chan size of 10 BTC
|
||||
<time> [ERR] FNDG: received funding error from <hex>: chan_id=<hex>, err=chan size of 10.00000001 BTC exceeds maximum chan size of 0.16777215 BTC
|
||||
<time> [ERR] RPCS: [/lnrpc.Lightning/OpenChannel]: received funding error from <hex>: chan_id=<hex>, err=chan size of 10.00000001 BTC exceeds maximum chan size of 0.16777215 BTC
|
||||
<time> [ERR] RPCS: unable to open channel to NodeKey(<hex>): received funding error from <hex>: chan_id=<hex>, err=chan size of 10.00000001 BTC exceeds maximum chan size of 0.16777215 BTC
|
||||
<time> [ERR] NTNF: unable to get hash from block with height <height>
|
||||
<time> [ERR] FNDG: Unable to add new channel <chan_point> with peer <hex>: canceled adding new channel
|
||||
<time> [ERR] RPCS: WS: error closing upgraded conn: write tcp4 <ip>-><ip>: write: connection reset by peer
|
||||
|
@ -142,6 +142,16 @@ func ErrChanTooSmall(chanSize, minChanSize btcutil.Amount) ReservationError {
|
||||
}
|
||||
}
|
||||
|
||||
// ErrChanTooLarge returns an error indicating that an incoming channel request
|
||||
// was too large. We'll reject any incoming channels if they're above our
|
||||
// configured value for the max channel size we'll accept.
|
||||
func ErrChanTooLarge(chanSize, maxChanSize btcutil.Amount) ReservationError {
|
||||
return ReservationError{
|
||||
fmt.Errorf("chan size of %v exceeds maximum chan size of %v",
|
||||
chanSize, maxChanSize),
|
||||
}
|
||||
}
|
||||
|
||||
// ErrHtlcIndexAlreadyFailed is returned when the HTLC index has already been
|
||||
// failed, but has not been committed by our commitment state.
|
||||
type ErrHtlcIndexAlreadyFailed uint64
|
||||
|
@ -185,6 +185,14 @@
|
||||
; channels smaller than this will be rejected, default value 20000.
|
||||
; minchansize=
|
||||
|
||||
; The largest channel size (in satoshis) that we should accept. Incoming
|
||||
; channels larger than this will be rejected. For non-Wumbo channels this
|
||||
; limit remains 16777215 satoshis by default as specified in BOLT-0002.
|
||||
; For wumbo channels this limit is 1,000,000,000 satoshis (10 BTC).
|
||||
; Set this config option explicitly to restrict your maximum channel size
|
||||
; to better align with your risk tolerance
|
||||
; maxchansize=
|
||||
|
||||
; The duration that a peer connection must be stable before attempting to send a
|
||||
; channel update to reenable or cancel a pending disables of the peer's channels
|
||||
; on the network. (default: 19m0s)
|
||||
|
@ -1168,6 +1168,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
||||
ZombieSweeperInterval: 1 * time.Minute,
|
||||
ReservationTimeout: 10 * time.Minute,
|
||||
MinChanSize: btcutil.Amount(cfg.MinChanSize),
|
||||
MaxChanSize: btcutil.Amount(cfg.MaxChanSize),
|
||||
MaxPendingChannels: cfg.MaxPendingChannels,
|
||||
RejectPush: cfg.RejectPush,
|
||||
NotifyOpenChannelEvent: s.channelNotifier.NotifyOpenChannelEvent,
|
||||
|
Loading…
Reference in New Issue
Block a user