lnd/lnwire/features_test.go
Carla Kirk-Cohen 019127c4f4
multi: add restriction on maximum feature bit in invoices
Base 32 encoded bolt 11 invoices only allow 10 bits to express the
length of the feature vector in a tagged field, so there is a much
lower limit on the values invoice custom features can hold.

Other places in the protocol are theoretically limited by the maximum
message size, but since we express a feature bit as u16 we don't need
to be concerned about this.

The decision is made to track maximum per-set in the feature manager,
which is conceptually aware of sets and then validate in lnwire/features
against some arbitrary maximum value provided to the caller to keep
the base features package unaware of sets.
2023-05-04 10:35:45 -04:00

531 lines
13 KiB
Go

package lnwire
import (
"bytes"
"math"
"reflect"
"sort"
"testing"
"github.com/stretchr/testify/require"
)
var testFeatureNames = map[FeatureBit]string{
0: "feature1",
3: "feature2",
4: "feature3",
5: "feature3",
}
func TestFeatureVectorSetUnset(t *testing.T) {
t.Parallel()
tests := []struct {
bits []FeatureBit
expectedFeatures []bool
}{
// No features are enabled if no bits are set.
{
bits: nil,
expectedFeatures: []bool{false, false, false, false, false, false, false, false},
},
// Test setting an even bit for an even-only bit feature. The
// corresponding odd bit should not be seen as set.
{
bits: []FeatureBit{0},
expectedFeatures: []bool{true, false, false, false, false, false, false, false},
},
// Test setting an odd bit for an even-only bit feature. The
// corresponding even bit should not be seen as set.
{
bits: []FeatureBit{1},
expectedFeatures: []bool{false, true, false, false, false, false, false, false},
},
// Test setting an even bit for an odd-only bit feature. The bit should
// be seen as set and the odd bit should not.
{
bits: []FeatureBit{2},
expectedFeatures: []bool{false, false, true, false, false, false, false, false},
},
// Test setting an odd bit for an odd-only bit feature. The bit should
// be seen as set and the even bit should not.
{
bits: []FeatureBit{3},
expectedFeatures: []bool{false, false, false, true, false, false, false, false},
},
// Test setting an even bit for even-odd pair feature. Both bits in the
// pair should be seen as set.
{
bits: []FeatureBit{4},
expectedFeatures: []bool{false, false, false, false, true, true, false, false},
},
// Test setting an odd bit for even-odd pair feature. Both bits in the
// pair should be seen as set.
{
bits: []FeatureBit{5},
expectedFeatures: []bool{false, false, false, false, true, true, false, false},
},
// Test setting an even bit for an unknown feature. The bit should be
// seen as set and the odd bit should not.
{
bits: []FeatureBit{6},
expectedFeatures: []bool{false, false, false, false, false, false, true, false},
},
// Test setting an odd bit for an unknown feature. The bit should be
// seen as set and the odd bit should not.
{
bits: []FeatureBit{7},
expectedFeatures: []bool{false, false, false, false, false, false, false, true},
},
}
fv := NewFeatureVector(nil, testFeatureNames)
for i, test := range tests {
for _, bit := range test.bits {
fv.Set(bit)
}
for j, expectedSet := range test.expectedFeatures {
if fv.HasFeature(FeatureBit(j)) != expectedSet {
t.Errorf("Expectation failed in case %d, bit %d", i, j)
break
}
}
for _, bit := range test.bits {
fv.Unset(bit)
}
}
}
// TestFeatureVectorRequiresFeature tests that if a feature vector only
// includes a required feature bit (it's even), then the RequiresFeature method
// will return true for both that bit as well as it's optional counter party.
func TestFeatureVectorRequiresFeature(t *testing.T) {
t.Parallel()
// Create a new feature vector with the features above, and set only
// the set of required bits. These will be all the even features
// referenced above.
fv := NewFeatureVector(nil, testFeatureNames)
fv.Set(0)
fv.Set(4)
// Next we'll query for those exact bits, these should show up as being
// required.
require.True(t, fv.RequiresFeature(0))
require.True(t, fv.RequiresFeature(4))
// If we query for the odd (optional) counter party to each of the
// features, the method should still return that the backing feature
// vector requires the feature to be set.
require.True(t, fv.RequiresFeature(1))
require.True(t, fv.RequiresFeature(5))
}
func TestFeatureVectorEncodeDecode(t *testing.T) {
t.Parallel()
tests := []struct {
bits []FeatureBit
expectedEncoded []byte
}{
{
bits: nil,
expectedEncoded: []byte{0x00, 0x00},
},
{
bits: []FeatureBit{2, 3, 7},
expectedEncoded: []byte{0x00, 0x01, 0x8C},
},
{
bits: []FeatureBit{2, 3, 8},
expectedEncoded: []byte{0x00, 0x02, 0x01, 0x0C},
},
}
for i, test := range tests {
fv := NewRawFeatureVector(test.bits...)
// Test that Encode produces the correct serialization.
buffer := new(bytes.Buffer)
err := fv.Encode(buffer)
if err != nil {
t.Errorf("Failed to encode feature vector in case %d: %v", i, err)
continue
}
encoded := buffer.Bytes()
if !bytes.Equal(encoded, test.expectedEncoded) {
t.Errorf("Wrong encoding in case %d: got %v, expected %v",
i, encoded, test.expectedEncoded)
continue
}
// Test that decoding then re-encoding produces the same result.
fv2 := NewRawFeatureVector()
err = fv2.Decode(bytes.NewReader(encoded))
if err != nil {
t.Errorf("Failed to decode feature vector in case %d: %v", i, err)
continue
}
buffer2 := new(bytes.Buffer)
err = fv2.Encode(buffer2)
if err != nil {
t.Errorf("Failed to re-encode feature vector in case %d: %v",
i, err)
continue
}
reencoded := buffer2.Bytes()
if !bytes.Equal(reencoded, test.expectedEncoded) {
t.Errorf("Wrong re-encoding in case %d: got %v, expected %v",
i, reencoded, test.expectedEncoded)
}
}
}
func TestFeatureVectorUnknownFeatures(t *testing.T) {
t.Parallel()
tests := []struct {
bits []FeatureBit
expectedUnknown []FeatureBit
}{
{
bits: nil,
expectedUnknown: nil,
},
// Since bits {0, 3, 4, 5} are known, and only even bits are considered
// required (according to the "it's OK to be odd rule"), that leaves
// {2, 6} as both unknown and required.
{
bits: []FeatureBit{0, 1, 2, 3, 4, 5, 6, 7},
expectedUnknown: []FeatureBit{2, 6},
},
}
for i, test := range tests {
rawVector := NewRawFeatureVector(test.bits...)
fv := NewFeatureVector(rawVector, testFeatureNames)
unknown := fv.UnknownRequiredFeatures()
// Sort to make comparison independent of order
sort.Slice(unknown, func(i, j int) bool {
return unknown[i] < unknown[j]
})
if !reflect.DeepEqual(unknown, test.expectedUnknown) {
t.Errorf("Wrong unknown features in case %d: got %v, expected %v",
i, unknown, test.expectedUnknown)
}
}
}
func TestFeatureNames(t *testing.T) {
t.Parallel()
tests := []struct {
bit FeatureBit
expectedName string
expectedKnown bool
}{
{
bit: 0,
expectedName: "feature1",
expectedKnown: true,
},
{
bit: 1,
expectedName: "unknown",
expectedKnown: false,
},
{
bit: 2,
expectedName: "unknown",
expectedKnown: false,
},
{
bit: 3,
expectedName: "feature2",
expectedKnown: true,
},
{
bit: 4,
expectedName: "feature3",
expectedKnown: true,
},
{
bit: 5,
expectedName: "feature3",
expectedKnown: true,
},
{
bit: 6,
expectedName: "unknown",
expectedKnown: false,
},
{
bit: 7,
expectedName: "unknown",
expectedKnown: false,
},
}
fv := NewFeatureVector(nil, testFeatureNames)
for _, test := range tests {
name := fv.Name(test.bit)
if name != test.expectedName {
t.Errorf("Name for feature bit %d is incorrect: "+
"expected %s, got %s", test.bit, name, test.expectedName)
}
known := fv.IsKnown(test.bit)
if known != test.expectedKnown {
t.Errorf("IsKnown for feature bit %d is incorrect: "+
"expected %v, got %v", test.bit, known, test.expectedKnown)
}
}
}
// TestIsRequired asserts that feature bits properly return their IsRequired
// status. We require that even features be required and odd features be
// optional.
func TestIsRequired(t *testing.T) {
optional := FeatureBit(1)
if optional.IsRequired() {
t.Fatalf("optional feature should not be required")
}
required := FeatureBit(0)
if !required.IsRequired() {
t.Fatalf("required feature should be required")
}
}
// TestFeatures asserts that the Features() method on a FeatureVector properly
// returns the set of feature bits it stores internally.
func TestFeatures(t *testing.T) {
tests := []struct {
name string
exp map[FeatureBit]struct{}
}{
{
name: "empty",
exp: map[FeatureBit]struct{}{},
},
{
name: "one",
exp: map[FeatureBit]struct{}{
5: {},
},
},
{
name: "several",
exp: map[FeatureBit]struct{}{
0: {},
5: {},
23948: {},
},
},
}
toRawFV := func(set map[FeatureBit]struct{}) *RawFeatureVector {
var bits []FeatureBit
for bit := range set {
bits = append(bits, bit)
}
return NewRawFeatureVector(bits...)
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
fv := NewFeatureVector(
toRawFV(test.exp), Features,
)
if !reflect.DeepEqual(fv.Features(), test.exp) {
t.Fatalf("feature mismatch, want: %v, got: %v",
test.exp, fv.Features())
}
})
}
}
func TestRawFeatureVectorOnlyContains(t *testing.T) {
t.Parallel()
features := []FeatureBit{
StaticRemoteKeyOptional,
AnchorsZeroFeeHtlcTxOptional,
ExplicitChannelTypeRequired,
}
fv := NewRawFeatureVector(features...)
require.True(t, fv.OnlyContains(features...))
require.False(t, fv.OnlyContains(features[:1]...))
}
func TestEqualRawFeatureVectors(t *testing.T) {
t.Parallel()
a := NewRawFeatureVector(
StaticRemoteKeyOptional,
AnchorsZeroFeeHtlcTxOptional,
ExplicitChannelTypeRequired,
)
b := a.Clone()
require.True(t, a.Equals(b))
b.Unset(ExplicitChannelTypeRequired)
require.False(t, a.Equals(b))
b.Set(ExplicitChannelTypeOptional)
require.False(t, a.Equals(b))
}
func TestIsEmptyFeatureVector(t *testing.T) {
t.Parallel()
fv := NewRawFeatureVector()
require.True(t, fv.IsEmpty())
fv.Set(StaticRemoteKeyOptional)
require.False(t, fv.IsEmpty())
fv.Unset(StaticRemoteKeyOptional)
require.True(t, fv.IsEmpty())
}
// TestValidatePairs tests that feature vectors can only set the required or
// optional feature bit in a pair, not both.
func TestValidatePairs(t *testing.T) {
t.Parallel()
rfv := NewRawFeatureVector(
StaticRemoteKeyOptional,
StaticRemoteKeyRequired,
)
require.Equal(t, ErrFeaturePairExists, rfv.ValidatePairs())
rfv = NewRawFeatureVector(
StaticRemoteKeyOptional,
PaymentAddrRequired,
)
require.Nil(t, rfv.ValidatePairs())
}
// TestValidateUpdate tests validation of an update to a feature vector.
func TestValidateUpdate(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
currentFeatures []FeatureBit
newFeatures []FeatureBit
maximumValue FeatureBit
err error
}{
{
name: "defined feature bit set, can include",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
err: nil,
},
{
name: "defined feature bit not already set",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
PaymentAddrRequired,
},
err: ErrFeatureStandard,
},
{
name: "known feature missing",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
PaymentAddrRequired,
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
err: ErrFeatureStandard,
},
{
name: "can set unknown feature",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
FeatureBit(1001),
},
err: nil,
},
{
name: "can unset unknown feature",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
FeatureBit(1001),
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
err: nil,
},
{
name: "at allowed maximum",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
100,
},
maximumValue: 100,
err: nil,
},
{
name: "above allowed maximum",
currentFeatures: []FeatureBit{
StaticRemoteKeyOptional,
},
newFeatures: []FeatureBit{
StaticRemoteKeyOptional,
101,
},
maximumValue: 100,
err: ErrFeatureBitMaximum,
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
currentFV := NewRawFeatureVector(
testCase.currentFeatures...,
)
newFV := NewRawFeatureVector(testCase.newFeatures...)
// Set maximum value if not populated in the test case.
maximumValue := testCase.maximumValue
if testCase.maximumValue == 0 {
maximumValue = math.MaxUint16
}
err := currentFV.ValidateUpdate(newFV, maximumValue)
require.ErrorIs(t, err, testCase.err)
})
}
}