lnd/feature/manager_internal_test.go
Keagan McClelland dad53b3f9a Revert "feature: make gossip queries compulsory"
This reverts commit 717facc202.

It turns out that we can't do this since it would result in
incompatibility with LDK. The spec has been updated to reallow
optional gossip queries so we revert this commit.
2024-04-02 16:43:27 -07:00

294 lines
6.5 KiB
Go

package feature
import (
"reflect"
"testing"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/require"
)
type managerTest struct {
name string
cfg Config
}
const unknownFeature lnwire.FeatureBit = 30
var testSetDesc = setDesc{
lnwire.DataLossProtectRequired: {
SetNodeAnn: {}, // I
},
lnwire.TLVOnionPayloadRequired: {
SetInit: {}, // I
SetNodeAnn: {}, // N
},
lnwire.StaticRemoteKeyRequired: {
SetInit: {}, // I
SetNodeAnn: {}, // N
},
}
var managerTests = []managerTest{
{
name: "default",
cfg: Config{},
},
{
name: "no tlv",
cfg: Config{
NoTLVOnion: true,
},
},
{
name: "no static remote key",
cfg: Config{
NoStaticRemoteKey: true,
},
},
{
name: "no tlv or static remote key",
cfg: Config{
NoTLVOnion: true,
NoStaticRemoteKey: true,
},
},
{
name: "anchors should disable anything dependent on it",
cfg: Config{
NoAnchors: true,
},
},
}
// TestManager asserts basic initialazation and operation of a feature manager,
// including that the proper features are removed in response to config changes.
func TestManager(t *testing.T) {
for _, test := range managerTests {
test := test
t.Run(test.name, func(t *testing.T) {
testManager(t, test)
})
}
}
func testManager(t *testing.T, test managerTest) {
m, err := newManager(test.cfg, testSetDesc)
require.NoError(t, err, "unable to create feature manager")
sets := []Set{
SetInit,
SetLegacyGlobal,
SetNodeAnn,
SetInvoice,
}
for _, set := range sets {
raw := m.GetRaw(set)
fv := m.Get(set)
fv2 := lnwire.NewFeatureVector(raw, lnwire.Features)
if !reflect.DeepEqual(fv, fv2) {
t.Fatalf("mismatch Get vs GetRaw, raw: %v vs fv: %v",
fv2, fv)
}
assertUnset := func(bit lnwire.FeatureBit) {
hasBit := fv.HasFeature(bit) || fv.HasFeature(bit^1)
if hasBit {
t.Fatalf("bit %v or %v is set", bit, bit^1)
}
}
// Assert that the manager properly unset the configured feature
// bits from all sets.
if test.cfg.NoTLVOnion {
assertUnset(lnwire.TLVOnionPayloadRequired)
assertUnset(lnwire.TLVOnionPayloadOptional)
}
if test.cfg.NoStaticRemoteKey {
assertUnset(lnwire.StaticRemoteKeyRequired)
assertUnset(lnwire.StaticRemoteKeyOptional)
}
if test.cfg.NoAnchors {
assertUnset(lnwire.ScriptEnforcedLeaseRequired)
assertUnset(lnwire.ScriptEnforcedLeaseOptional)
}
assertUnset(unknownFeature)
}
// Do same basic sanity checks on features that are always present.
nodeFeatures := m.Get(SetNodeAnn)
assertSet := func(bit lnwire.FeatureBit) {
has := nodeFeatures.HasFeature(bit)
if !has {
t.Fatalf("node features don't advertised %v", bit)
}
}
assertSet(lnwire.DataLossProtectRequired)
if !test.cfg.NoTLVOnion {
assertSet(lnwire.TLVOnionPayloadRequired)
}
if !test.cfg.NoStaticRemoteKey {
assertSet(lnwire.StaticRemoteKeyRequired)
}
}
// TestUpdateFeatureSets tests validation of the update of various features in
// each of our sets, asserting that the feature set is not partially modified
// if one set in incorrectly specified.
func TestUpdateFeatureSets(t *testing.T) {
t.Parallel()
// Use a reduced set description to make reasoning about our sets
// easier.
setDesc := setDesc{
lnwire.DataLossProtectRequired: {
SetInit: {}, // I
SetNodeAnn: {}, // N
},
lnwire.GossipQueriesOptional: {
SetNodeAnn: {}, // N
},
}
testCases := []struct {
name string
features map[Set]*lnwire.RawFeatureVector
config Config
err error
}{
{
name: "unknown set",
features: map[Set]*lnwire.RawFeatureVector{
setSentinel + 1: lnwire.NewRawFeatureVector(),
},
err: ErrUnknownSet,
},
{
name: "invalid pairwise feature",
features: map[Set]*lnwire.RawFeatureVector{
SetNodeAnn: lnwire.NewRawFeatureVector(
lnwire.FeatureBit(1000),
lnwire.FeatureBit(1001),
),
},
err: lnwire.ErrFeaturePairExists,
},
{
name: "error in one set",
features: map[Set]*lnwire.RawFeatureVector{
SetNodeAnn: lnwire.NewRawFeatureVector(
lnwire.FeatureBit(1000),
lnwire.FeatureBit(1001),
),
SetInit: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
),
},
err: lnwire.ErrFeaturePairExists,
},
{
name: "update existing sets ok",
features: map[Set]*lnwire.RawFeatureVector{
SetInit: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
lnwire.FeatureBit(1001),
),
SetNodeAnn: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
lnwire.GossipQueriesOptional,
lnwire.FeatureBit(1000),
),
},
},
{
name: "update new, valid set ok",
features: map[Set]*lnwire.RawFeatureVector{
SetInvoice: lnwire.NewRawFeatureVector(
lnwire.FeatureBit(1001),
),
},
},
{
name: "missing configured feature",
features: map[Set]*lnwire.RawFeatureVector{
SetInit: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
),
SetNodeAnn: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
lnwire.GossipQueriesOptional,
),
},
config: Config{
CustomFeatures: map[Set][]lnwire.FeatureBit{
SetInit: {
lnwire.FeatureBit(333),
},
},
},
err: ErrFeatureConfigured,
},
{
name: "valid",
features: map[Set]*lnwire.RawFeatureVector{
SetInit: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
),
SetNodeAnn: lnwire.NewRawFeatureVector(
lnwire.DataLossProtectRequired,
lnwire.GossipQueriesOptional,
lnwire.FeatureBit(500),
),
SetInvoice: lnwire.NewRawFeatureVector(
lnwire.FeatureBit(333),
),
},
config: Config{
CustomFeatures: map[Set][]lnwire.FeatureBit{
SetInvoice: {
lnwire.FeatureBit(333),
},
},
},
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
featureMgr, err := newManager(testCase.config, setDesc)
require.NoError(t, err)
err = featureMgr.UpdateFeatureSets(testCase.features)
require.ErrorIs(t, err, testCase.err)
// Compare the feature manager's sets to the updated
// set if no error was hit, otherwise assert that it
// is unchanged.
expected := testCase.features
actual := featureMgr
if err != nil {
originalMgr, err := newManager(
testCase.config, setDesc,
)
require.NoError(t, err)
expected = originalMgr.fsets
}
for set, expectedFeatures := range expected {
actualSet := actual.GetRaw(set)
require.True(t,
actualSet.Equals(expectedFeatures))
}
})
}
}