mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
019127c4f4
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.
531 lines
13 KiB
Go
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)
|
|
})
|
|
}
|
|
}
|