lnd/lncfg/protocol.go
Olaoluwa Osuntokun 335b75981f lncfg+peer+server: add protocol.onion-msg-relay-all to bypass channel gate
Add a new protocol option, protocol.onion-msg-relay-all, that controls
whether incoming onion messages are required to come from peers with a
fully open channel. The default is false, which preserves the existing
behavior: the channel-presence gate drops messages from peers with no
channel before the rate limiters are consulted, so a new no-cost
identity cannot burn any per-peer byte budget and saturate the global
bucket. Setting the flag to true skips the gate so that onion messages
from any peer are admitted into the per-peer + global IngressLimiter
pipeline.

The flag is plumbed through ProtocolOptions in both the default and
integration build variants of lncfg/protocol*.go, threaded into the
peer subsystem as peer.Config.OnionRelayAll, and wired by the server
from s.cfg.ProtocolOptions.OnionMsgRelayAll alongside the existing
OnionLimiter field. allowOnionMessage gains a relayAll bool parameter;
the gate check becomes "if \!relayAll && \!hasChannel { drop }" so the
semantics of hasChannel stay pure — it still means "this peer has a
channel" — and the policy toggle lives entirely in the caller's
configuration rather than being spread across gate-state and flag
state.

sample-lnd.conf gains a commented-out entry for the new option with
the default value and an operator-facing note that enabling it trades
the Sybil-resistance property of the gate for reachability to peers
with whom we have no channel.

A new TestAllowOnionMessageRelayAll unit test exercises the four
(hasChannel, relayAll) combinations at the helper level, including
the key new behavior — a peer with hasChannel=false being rejected
under relayAll=false and admitted into the limiter under
relayAll=true — and the nil-limiter path under relayAll=true, which
must still accept. The existing allowOnionMessage tests were
extended with the new parameter set to false so they continue to
assert the gate semantics unchanged.
2026-04-15 13:23:50 -07:00

225 lines
11 KiB
Go

//go:build !integration
package lncfg
import (
"github.com/lightningnetwork/lnd/feature"
"github.com/lightningnetwork/lnd/lnwire"
)
// ProtocolOptions is a struct that we use to be able to test backwards
// compatibility of protocol additions, while defaulting to the latest within
// lnd, or to enable experimental protocol changes.
//
//nolint:ll
type ProtocolOptions struct {
// LegacyProtocol is a sub-config that houses all the legacy protocol
// options. These are mostly used for integration tests as most modern
// nodes should always run with them on by default.
LegacyProtocol `group:"legacy" namespace:"legacy"`
// ExperimentalProtocol is a sub-config that houses any experimental
// protocol features that also require a build-tag to activate.
ExperimentalProtocol
// WumboChans should be set if we want to enable support for wumbo
// (channels larger than 0.16 BTC) channels, which is the opposite of
// mini.
WumboChans bool `long:"wumbo-channels" description:"if set, then lnd will create and accept requests for channels larger chan 0.16 BTC"`
// TaprootChans should be set if we want to enable support for the
// experimental simple taproot chans commitment type.
TaprootChans bool `long:"simple-taproot-chans" description:"if set, then lnd will create and accept requests for channels using the simple taproot commitment type"`
// TaprootOverlayChans should be set if we want to enable support for
// the experimental taproot overlay chan type.
TaprootOverlayChans bool `long:"simple-taproot-overlay-chans" description:"if set, then lnd will create and accept requests for channels using the taproot overlay commitment type"`
// RbfCoopClose should be set if we want to signal that we support for
// the new experimental RBF coop close feature.
RbfCoopClose bool `long:"rbf-coop-close" description:"if set, then lnd will signal that it supports the new RBF based coop close protocol"`
// NoAnchors should be set if we don't want to support opening or accepting
// channels having the anchor commitment type.
NoAnchors bool `long:"no-anchors" description:"disable support for anchor commitments"`
// NoScriptEnforcedLease should be set if we don't want to support
// opening or accepting channels having the script enforced commitment
// type for leased channel.
NoScriptEnforcedLease bool `long:"no-script-enforced-lease" description:"disable support for script enforced lease commitments"`
// OptionScidAlias should be set if we want to signal the
// option-scid-alias feature bit. This allows scid aliases and the
// option-scid-alias channel-type.
OptionScidAlias bool `long:"option-scid-alias" description:"enable support for option_scid_alias channels"`
// OptionZeroConf should be set if we want to signal the zero-conf
// feature bit.
OptionZeroConf bool `long:"zero-conf" description:"enable support for zero-conf channels, must have option-scid-alias set also"`
// NoOptionAnySegwit should be set to true if we don't want to use any
// Taproot (and beyond) addresses for co-op closing.
NoOptionAnySegwit bool `long:"no-any-segwit" description:"disallow using any segwit witness version as a co-op close address"`
// NoTimestampQueryOption should be set to true if we don't want our
// syncing peers to also send us the timestamps of announcement messages
// when we send them a channel range query. Setting this to true will
// also mean that we won't respond with timestamps if requested by our
// peers.
NoTimestampQueryOption bool `long:"no-timestamp-query-option" description:"do not query syncing peers for announcement timestamps and do not respond with timestamps if requested"`
// NoRouteBlindingOption disables forwarding of payments in blinded routes.
NoRouteBlindingOption bool `long:"no-route-blinding" description:"do not forward payments that are a part of a blinded route"`
// NoOnionMessagesOption disables onion message forwarding.
NoOnionMessagesOption bool `long:"no-onion-messages" description:"disable support for onion messaging"`
// OnionMsgPeerKbps is the maximum sustained onion message ingress
// bandwidth, in decimal kilobits per second (1 Kbps = 1000 bits/s),
// that will be accepted from any single peer. Setting this to zero,
// together with a zero burst, disables the per-peer onion message
// rate limiter.
OnionMsgPeerKbps uint64 `long:"onion-msg-peer-kbps" description:"max onion message ingress rate from a single peer, in decimal kilobits per second; set both this and onion-msg-peer-burst-bytes to 0 to disable the per-peer limiter"`
// OnionMsgPeerBurstBytes is the token bucket depth, in bytes, used
// by the per-peer onion message rate limiter. A value of zero,
// paired with a zero rate, disables the per-peer limiter.
OnionMsgPeerBurstBytes uint64 `long:"onion-msg-peer-burst-bytes" description:"token bucket burst for the per-peer onion message limiter, in bytes; set both this and onion-msg-peer-kbps to 0 to disable the per-peer limiter"`
// OnionMsgGlobalKbps is the maximum sustained onion message ingress
// bandwidth, in decimal kilobits per second, that will be accepted
// across all peers combined. Setting this to zero, together with a
// zero burst, disables the global onion message rate limiter.
OnionMsgGlobalKbps uint64 `long:"onion-msg-global-kbps" description:"max onion message ingress rate across all peers combined, in decimal kilobits per second; set both this and onion-msg-global-burst-bytes to 0 to disable the global limiter"`
// OnionMsgGlobalBurstBytes is the token bucket depth, in bytes, used
// by the global onion message rate limiter. A value of zero, paired
// with a zero rate, disables the global limiter.
OnionMsgGlobalBurstBytes uint64 `long:"onion-msg-global-burst-bytes" description:"token bucket burst for the global onion message limiter, in bytes; set both this and onion-msg-global-kbps to 0 to disable the global limiter"`
// OnionMsgRelayAll disables the channel-presence gate on the onion
// message ingress path. When false (the default), incoming onion
// messages from peers that do not have at least one fully open
// channel with us are dropped before the rate limiters are
// consulted: without a funded channel, a new peer identity is free
// and the global rate limiter alone is easy to saturate. Setting
// this to true admits onion messages from any peer into the
// limiter pipeline, at the cost of that Sybil-resistance property.
OnionMsgRelayAll bool `long:"onion-msg-relay-all" description:"accept incoming onion messages from peers with no fully open channel; by default only peers with at least one active channel are admitted to the onion message ingress path"`
// NoExperimentalAccountabilityOption disables experimental accountability.
NoExperimentalAccountabilityOption bool `long:"no-experimental-accountability" description:"do not forward experimental accountability signals"`
// NoExperimentalEndorsementOption is the deprecated name for
// NoExperimentalAccountabilityOption. It is hidden and will be removed
// in a future release.
NoExperimentalEndorsementOption bool `long:"no-experimental-endorsement" hidden:"true" description:"deprecated: use no-experimental-accountability instead"`
// CustomMessage allows the custom message APIs to handle messages with
// the provided protocol numbers, which fall outside the custom message
// number range.
CustomMessage []uint16 `long:"custom-message" description:"allows the custom message apis to send and report messages with the protocol number provided that fall outside of the custom message number range."`
// CustomInit specifies feature bits to advertise in the node's init
// message.
CustomInit []uint16 `long:"custom-init" description:"custom feature bits — numbers defined in BOLT 9 — to advertise in the node's init message"`
// CustomNodeAnn specifies custom feature bits to advertise in the
// node's announcement message.
CustomNodeAnn []uint16 `long:"custom-nodeann" description:"custom feature bits — numbers defined in BOLT 9 — to advertise in the node's announcement message"`
// CustomInvoice specifies custom feature bits to advertise in the
// node's invoices.
CustomInvoice []uint16 `long:"custom-invoice" description:"custom feature bits — numbers defined in BOLT 9 — to advertise in the node's invoices"`
}
// Wumbo returns true if lnd should permit the creation and acceptance of wumbo
// channels.
func (l *ProtocolOptions) Wumbo() bool {
return l.WumboChans
}
// NoAnchorCommitments returns true if we have disabled support for the anchor
// commitment type.
func (l *ProtocolOptions) NoAnchorCommitments() bool {
return l.NoAnchors
}
// NoScriptEnforcementLease returns true if we have disabled support for the
// script enforcement commitment type for leased channels.
func (l *ProtocolOptions) NoScriptEnforcementLease() bool {
return l.NoScriptEnforcedLease
}
// ScidAlias returns true if we have enabled the option-scid-alias feature bit.
func (l *ProtocolOptions) ScidAlias() bool {
return l.OptionScidAlias
}
// ZeroConf returns true if we have enabled the zero-conf feature bit.
func (l *ProtocolOptions) ZeroConf() bool {
return l.OptionZeroConf
}
// NoAnySegwit returns true if we don't signal that we understand other newer
// segwit witness versions for co-op close addresses.
func (l *ProtocolOptions) NoAnySegwit() bool {
return l.NoOptionAnySegwit
}
// NoTimestampsQuery returns true if we should not ask our syncing peers to also
// send us the timestamps of announcement messages when we send them a channel
// range query, and it also means that we will not respond with timestamps if
// requested by our peer.
func (l *ProtocolOptions) NoTimestampsQuery() bool {
return l.NoTimestampQueryOption
}
// NoRouteBlinding returns true if forwarding of blinded payments is disabled.
func (l *ProtocolOptions) NoRouteBlinding() bool {
return l.NoRouteBlindingOption
}
// NoOnionMessages returns true if onion messaging is disabled.
func (l *ProtocolOptions) NoOnionMessages() bool {
return l.NoOnionMessagesOption
}
// NoExpAccountability returns true if experimental accountability should be
// disabled. It also checks the deprecated NoExperimentalEndorsementOption for
// backwards compatibility.
func (l *ProtocolOptions) NoExpAccountability() bool {
return l.NoExperimentalAccountabilityOption ||
l.NoExperimentalEndorsementOption
}
// NoQuiescence returns true if quiescence is disabled.
func (l *ProtocolOptions) NoQuiescence() bool {
return false
}
// CustomMessageOverrides returns the set of protocol messages that we override
// to allow custom handling.
func (p ProtocolOptions) CustomMessageOverrides() []uint16 {
return p.CustomMessage
}
// CustomFeatures returns a custom set of feature bits to advertise.
func (p ProtocolOptions) CustomFeatures() map[feature.Set][]lnwire.FeatureBit {
customFeatures := make(map[feature.Set][]lnwire.FeatureBit)
setFeatures := func(set feature.Set, bits []uint16) {
for _, customFeature := range bits {
customFeatures[set] = append(
customFeatures[set],
lnwire.FeatureBit(customFeature),
)
}
}
setFeatures(feature.SetInit, p.CustomInit)
setFeatures(feature.SetNodeAnn, p.CustomNodeAnn)
setFeatures(feature.SetInvoice, p.CustomInvoice)
return customFeatures
}