-
Notifications
You must be signed in to change notification settings - Fork 2.2k
multi: use relaxed feature bit Set
method for peer features
#9884
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
multi: use relaxed feature bit Set
method for peer features
#9884
Conversation
Important Review skippedAuto reviews are limited to specific labels. 🏷️ Labels to auto review (1)
Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
b761069
to
80db68a
Compare
@saubyk for reviewers 🙏 |
// should never set both the required and optional bits of a | ||
// 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah the spec is confusing - but based on the description, when we are setting the bits we should always make sure not to set both, and only when we are receiving the bits we can then treat the feature as mandatory? so we should still have this check when we are parseQueryRoutesRequest
or extractIntentFromSendRequest
since we are creating the bits?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but we are not creating the bits in parseQueryRoutesRequest
and extractIntentFromSendRequest
. For those we are reading bits we got from the peer in an invoice. When we set our own bits, we do only set one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for sending payments that's only true when it's using PaymentRequest
aka the invoice, as the caller can set DestFeatures
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure but i think it's ok for us to be lenient for any externally created feature vector. ie, including those set in DestFeatures - as perhaps they were extracted from a payReq by the caller.
Ie, i think we only need to be strict with the ones that we ourselves are generating from within LND and including in invoices/feature vectors we create.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather keep the safeSet method and also log a warning and then proceed, this makes us at least aware that something is wrong on the invoice side for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure but i think it's ok for us to be lenient for any externally created feature vector. ie, including those set in DestFeatures - as perhaps they were extracted from a payReq by the caller.
My assumption is, when the user is using the payReq, it's from an invoice outside of lnd's control, so we skip the check, which is most of the case. When the user is touching the custom DestFeatures, it's another level of usage - and since it's customizing, it won't be difficult to remove the wrong feature param. Based on the specs we should not set both.
BUT, the spec is clearly broken, as sender is using receiver's data, and apparently the MUST NOT set both the optional and mandatory bits
clearly cannot be enforced as the receiver doesn't care. So I think this approach works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I agree with @ziggie1984. Why not keep the SafeSet
, and then check for ErrFeaturePairExists
error in places where we're reading the features? This way, it doesn't seem like we're normalizing this behavior since we all agree the spec isn't clear on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the fix! We should update the test here tho,
lnd/lnrpc/routerrpc/router_backend_test.go
Line 497 in c6d6d4c
func TestExtractIntentFromSendRequest(t *testing.T) { |
// should never set both the required and optional bits of a | ||
// 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for sending payments that's only true when it's using PaymentRequest
aka the invoice, as the caller can set DestFeatures
.
if err != nil { | ||
return nil, err | ||
} | ||
features := UnmarshalFeatures(rpcPayReq.DestFeatures) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sender is setting the field, so I think we should error out in this case? payReq.Features
is what's extracted from the invoice at L1067, and it was not validated I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see comment below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and it was not validated I think.
i think this is the point though right? ie, not to be too strict with feature vectors we are parsing. As long as we then read it as "required", things are fine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as we then read it as "required", things are fine
I think so, tho not sure it was skipped intentionally or accidentally, but yeah what i meant was the features parsed from an outside source has already been skipped so no need to worry about that.
Here we add a test case to TestExtractIntentFromSendRequest which shows that an error is returned if the destination feature bit vector of a payment request contains both the required and optional bits for a given feature. This will be updated in an upcoming commit to be less strict.
The spec says: `The origin node MUST NOT set both the optional and mandatory bits`. and so this is why we have the SafeSet method which prevents us from accidentally setting both optional and required bits for any of our own feature bits. But the spec then also says `if both the optional and the mandatory feature bits in a pair are set, the feature should be treated as mandatory.` which means that when we read the feature vectors of our peers or from a payment request, we should be a bit less strict and not error out. We should just set both bits which will result in "IsRequired" returning true. Update the `TestExtractIntentFromSendRequest` test to show the new behaviour.
80db68a
to
e68b882
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the review @yyforyongyu 🙏
updated that test as per suggestion.
Let me know your thoughts on the discussion
// should never set both the required and optional bits of a | ||
// 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure but i think it's ok for us to be lenient for any externally created feature vector. ie, including those set in DestFeatures - as perhaps they were extracted from a payReq by the caller.
Ie, i think we only need to be strict with the ones that we ourselves are generating from within LND and including in invoices/feature vectors we create.
Thoughts?
if err != nil { | ||
return nil, err | ||
} | ||
features := UnmarshalFeatures(rpcPayReq.DestFeatures) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see comment below
if err != nil { | ||
return nil, err | ||
} | ||
features := UnmarshalFeatures(rpcPayReq.DestFeatures) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and it was not validated I think.
i think this is the point though right? ie, not to be too strict with feature vectors we are parsing. As long as we then read it as "required", things are fine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM🦾
As long as every implementation is following the spec there's no issue relaxing setting the feature flags,
The receiving node:
- if both the optional and the mandatory feature bits in a pair are set, the feature should be treated as mandatory.
@@ -809,6 +809,27 @@ func TestExtractIntentFromSendRequest(t *testing.T) { | |||
valid: false, | |||
expectedErrorMsg: "self-payments not allowed", | |||
}, | |||
{ | |||
name: "Required and optional feature bits set", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice one!, left a feedback.
@@ -828,8 +828,7 @@ func TestExtractIntentFromSendRequest(t *testing.T) { | |||
lnrpc.FeatureBit_GOSSIP_QUERIES_REQ, | |||
}, | |||
}, | |||
valid: false, | |||
expectedErrorMsg: "feature pair exists", | |||
valid: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🫡
@@ -1348,24 +1336,26 @@ func MarshalFeatures(feats *lnwire.FeatureVector) []lnrpc.FeatureBit { | |||
// UnmarshalFeatures converts a list of uint32's into a valid feature vector. | |||
// This method checks that feature bit pairs aren't assigned together, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to update this comment then because it says ...checks that feature bit pairs aren't assigned together
?
// should never set both the required and optional bits of a | ||
// 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I agree with @ziggie1984. Why not keep the SafeSet
, and then check for ErrFeaturePairExists
error in places where we're reading the features? This way, it doesn't seem like we're normalizing this behavior since we all agree the spec isn't clear on this.
@ziggie1984: review reminder |
The spec says:
The origin node MUST NOT set both the optional and mandatory bits
. and so this is why we have the SafeSet method whichprevents us from accidentally setting both optional and required bits
for any of our own feature bits. But the spec then also says
if both the optional and the mandatory feature bits in a pair are set, the feature should be treated as mandatory.
which means that when we readthe feature vectors of our peers or from a payment request, we should be
a bit less strict and not error out. We should just set both bits which
will result in "IsRequired" returning true.
Fixes #9855
The error in #9855 could appear if the user was using
estimateroutefee
with aninvoice containing both required&optional bits for a feature or if the SendPaymentV2 method
was used via the proto fields (ie, not setting the PayReq field).