Merge pull request #9884 from ellemouton/relaxFeatureBitCheck

multi: use relaxed feature bit `Set` method for peer features
This commit is contained in:
Yong authored and GitHub committed 2025-09-10 17:22:16 +08:00
commit d1d3a82010
5 files changed
+41 -30

No files matched your search

@@ -223,6 +223,9 @@ reader of a payment request.
record](https://github.com/lightningnetwork/lnd/pull/9897) on the record](https://github.com/lightningnetwork/lnd/pull/9897) on the
`channel_update` message and handle it explicitly throughout the code base `channel_update` message and handle it explicitly throughout the code base
instead of extracting it from the TLV stream at various call-sites. instead of extracting it from the TLV stream at various call-sites.
* [Don't error out](https://github.com/lightningnetwork/lnd/pull/9884) if an
invoice's feature vector contain both the required and optional versions of a
feature bit. In those cases, just treat the feature as mandatory.
* [Require invoices to include a payment address or blinded paths](https://github.com/lightningnetwork/lnd/pull/9752) * [Require invoices to include a payment address or blinded paths](https://github.com/lightningnetwork/lnd/pull/9752)
to comply with updated BOLT 11 specifications before sending payments. to comply with updated BOLT 11 specifications before sending payments.
+15 -25
View File
@@ -365,10 +365,7 @@ func (r *RouterBackend) parseQueryRoutesRequest(in *lnrpc.QueryRoutesRequest) (
} }
// Parse destination feature bits. // Parse destination feature bits.
destinationFeatures, err = UnmarshalFeatures(in.DestFeatures) destinationFeatures = UnmarshalFeatures(in.DestFeatures)
if err != nil {
return nil, err
}
} }
// We need to subtract the final delta before passing it into path // We need to subtract the final delta before passing it into path
@@ -510,10 +507,7 @@ func unmarshalBlindedPayment(rpcPayment *lnrpc.BlindedPaymentPath) (
return nil, err return nil, err
} }
features, err := UnmarshalFeatures(rpcPayment.Features) features := UnmarshalFeatures(rpcPayment.Features)
if err != nil {
return nil, err
}
return &routing.BlindedPayment{ return &routing.BlindedPayment{
BlindedPath: path, BlindedPath: path,
@@ -1148,10 +1142,7 @@ func (r *RouterBackend) extractIntentFromSendRequest(
payIntent.Amount = reqAmt payIntent.Amount = reqAmt
// Parse destination feature bits. // Parse destination feature bits.
features, err := UnmarshalFeatures(rpcPayReq.DestFeatures) features := UnmarshalFeatures(rpcPayReq.DestFeatures)
if err != nil {
return nil, err
}
// Validate the features if any was specified. // Validate the features if any was specified.
if features != nil { if features != nil {
@@ -1173,10 +1164,7 @@ func (r *RouterBackend) extractIntentFromSendRequest(
lnrpc.FeatureBit_AMP_OPT, lnrpc.FeatureBit_AMP_OPT,
} }
features, err = UnmarshalFeatures(ampFeatures) features = UnmarshalFeatures(ampFeatures)
if err != nil {
return nil, err
}
} }
// First make sure the destination supports AMP. // First make sure the destination supports AMP.
@@ -1372,24 +1360,26 @@ func MarshalFeatures(feats *lnwire.FeatureVector) []lnrpc.FeatureBit {
// UnmarshalFeatures converts a list of uint32's into a valid feature vector. // UnmarshalFeatures converts a list of uint32's into a valid feature vector.
// This method checks that feature bit pairs aren't assigned together, and // This method checks that feature bit pairs aren't assigned together, and
// validates transitive dependencies. // validates transitive dependencies.
func UnmarshalFeatures( func UnmarshalFeatures(rpcFeatures []lnrpc.FeatureBit) *lnwire.FeatureVector {
rpcFeatures []lnrpc.FeatureBit) (*lnwire.FeatureVector, error) {
// If no destination features are specified we'll return nil to signal // If no destination features are specified we'll return nil to signal
// that the router should try to use the graph as a fallback. // that the router should try to use the graph as a fallback.
if rpcFeatures == nil { if rpcFeatures == nil {
return nil, nil return nil
} }
raw := lnwire.NewRawFeatureVector() raw := lnwire.NewRawFeatureVector()
for _, bit := range rpcFeatures { for _, bit := range rpcFeatures {
err := raw.SafeSet(lnwire.FeatureBit(bit)) // Even though the spec says that the writer of a feature vector
if err != nil { // should never set both the required and optional bits of a
return nil, err // feature, it also says that if we receive a vector with both
} // bits set, then we should just treat the feature as required.
// Therefore, we don't use SafeSet here when parsing a peer's
// feature bits and just set the feature no matter what so that
// if both are set then IsRequired returns true.
raw.Set(lnwire.FeatureBit(bit))
} }
return lnwire.NewFeatureVector(raw, lnwire.Features), nil return lnwire.NewFeatureVector(raw, lnwire.Features)
} }
// ValidatePayReqExpiry checks if the passed payment request has expired. In // ValidatePayReqExpiry checks if the passed payment request has expired. In
+21
View File
@@ -881,6 +881,27 @@ func TestExtractIntentFromSendRequest(t *testing.T) {
valid: false, valid: false,
expectedErrorMsg: "self-payments not allowed", expectedErrorMsg: "self-payments not allowed",
}, },
{
name: "Required and optional feature bits set",
backend: &RouterBackend{
MaxTotalTimelock: 1000,
ShouldSetExpEndorsement: func() bool {
return false
},
},
sendReq: &SendPaymentRequest{
Dest: destNodeBytes,
Amt: int64(paymentAmount),
PaymentHash: make([]byte, 32),
MaxParts: 10,
MaxShardSizeMsat: 30_000_000,
DestFeatures: []lnrpc.FeatureBit{
lnrpc.FeatureBit_GOSSIP_QUERIES_OPT,
lnrpc.FeatureBit_GOSSIP_QUERIES_REQ,
},
},
valid: true,
},
{ {
name: "Valid send req parameters, payment settled", name: "Valid send req parameters, payment settled",
backend: &RouterBackend{ backend: &RouterBackend{
+1 -1
View File
@@ -446,7 +446,7 @@ func (fv RawFeatureVector) Equals(other *RawFeatureVector) bool {
return true return true
} }
// Merges sets all feature bits in other on the receiver's feature vector. // Merge sets all feature bits in other on the receiver's feature vector.
func (fv *RawFeatureVector) Merge(other *RawFeatureVector) error { func (fv *RawFeatureVector) Merge(other *RawFeatureVector) error {
for bit := range other.features { for bit := range other.features {
err := fv.SafeSet(bit) err := fv.SafeSet(bit)
+1 -4
View File
@@ -5827,12 +5827,9 @@ func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPayme
} }
// Unmarshal any custom destination features. // Unmarshal any custom destination features.
payIntent.destFeatures, err = routerrpc.UnmarshalFeatures( payIntent.destFeatures = routerrpc.UnmarshalFeatures(
rpcPayReq.DestFeatures, rpcPayReq.DestFeatures,
) )
if err != nil {
return payIntent, err
}
return payIntent, nil return payIntent, nil
} }