mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
292 lines
6.4 KiB
Go
292 lines
6.4 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.TLVOnionPayloadOptional: {
|
|
SetInit: {}, // I
|
|
SetNodeAnn: {}, // N
|
|
},
|
|
lnwire.StaticRemoteKeyOptional: {
|
|
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.TLVOnionPayloadOptional)
|
|
}
|
|
if test.cfg.NoStaticRemoteKey {
|
|
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.DataLossProtectOptional)
|
|
if !test.cfg.NoTLVOnion {
|
|
assertSet(lnwire.TLVOnionPayloadRequired)
|
|
}
|
|
if !test.cfg.NoStaticRemoteKey {
|
|
assertSet(lnwire.StaticRemoteKeyOptional)
|
|
}
|
|
}
|
|
|
|
// 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))
|
|
}
|
|
})
|
|
}
|
|
}
|