-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
routerrpc: add validation to MPP params #9603
base: master
Are you sure you want to change the base?
routerrpc: add validation to MPP params #9603
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:
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 (
|
91c0f3d
to
f4513f2
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 PR! Left a few comments re the format and test.
itest/lnd_mpp_test.go
Outdated
FeeLimitMsat: noFeeLimitMsat, | ||
PaymentRequest: payReq, | ||
MaxParts: 10, | ||
MaxShardSizeMsat: 30_000_000, |
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.
why do we need to set MaxShardSizeMsat
here?
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.
To validate that when the mpp params permit sending the full payment amount, the payment is successfully settled. I'll add a comment in the test to make this clear.
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.
Instead of just adding here the MaxShardSizeMsat
please create a separate test which tests exactly the validation. This separates the test cases nicely and can be build integrating your test above testValidateMPPParams
. First trying a lower shard amount and then increasing it and succeeding.
itest/lnd_mpp_test.go
Outdated
// testValidateMPPParams validates that a payment with multipath parameters | ||
// exceeding the allowed maximum amount fails with the expected error. | ||
func testValidateMPPParams( | ||
ht *lntest.HarnessTest, mts *mppTestScenario, paymentAmt btcutil.Amount, |
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 format is not quite right, see https://github.com/lightningnetwork/lnd/blob/master/docs/code_formatting_rules.md#wrapping-long-function-definitions
itest/lnd_mpp_test.go
Outdated
@@ -53,6 +85,9 @@ func testSendMultiPathPayment(ht *lntest.HarnessTest) { | |||
mts.alice, mts.dave, expectedPolicy, chanPointAliceDave, false, | |||
) | |||
|
|||
// Validate that multipath payment parameters are enforced correctly. | |||
testValidateMPPParams(ht, mts, paymentAmt) |
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.
Also check this in testSendToRouteMultiPath
?
f4513f2
to
480c880
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.
LGTM🌹
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, left some comments.
itest/lnd_mpp_test.go
Outdated
// exceeding the allowed maximum amount fails with the expected error. | ||
func testValidateMPPParams(ht *lntest.HarnessTest, mts *mppTestScenario, | ||
paymentAmt btcutil.Amount) { | ||
// Create an invoice from Bob for the payment. |
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.
Nit: New line above
itest/lnd_mpp_test.go
Outdated
FeeLimitMsat: noFeeLimitMsat, | ||
PaymentRequest: payReq, | ||
MaxParts: 10, | ||
MaxShardSizeMsat: 30_000_000, |
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.
Instead of just adding here the MaxShardSizeMsat
please create a separate test which tests exactly the validation. This separates the test cases nicely and can be build integrating your test above testValidateMPPParams
. First trying a lower shard amount and then increasing it and succeeding.
ae6dfb4
to
791dd97
Compare
lnrpc/routerrpc/router_backend.go
Outdated
// payment amount. In other words, the parameters are invalid if | ||
// they do not permit sending the full payment amount. | ||
if rpcPayReq.MaxShardSizeMsat > 0 { | ||
if payIntent.MaxParts > MaxPartsUpperLimit { |
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.
this check should be moved to where we assign payIntent.MaxParts
at L863 so we can exit early.
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.
Let's put it at L873, since the MPP will only be used when MaxShardSizeMsat > 0
. So, if MaxShardSizeMsat
is not being set, then there is no issue with the value of MaxParts
?
lnrpc/routerrpc/router_backend.go
Outdated
// Validate that the MPP parameters are compatible with the | ||
// payment amount. In other words, the parameters are invalid if | ||
// they do not permit sending the full payment amount. | ||
if rpcPayReq.MaxShardSizeMsat > 0 { |
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.
think we should check payIntent.MaxShardAmt
for clarity.
itest/lnd_mpp_test.go
Outdated
// \ / | ||
// \__ Dave ____/ | ||
// | ||
paymentAmt := mts.setupSendPaymentCase() |
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.
For a simple test like this, I'd say we use ht.CreateSimpleNetwork
instead as we are not testing the routing behavior here.
itest/lnd_mpp_test.go
Outdated
// | ||
paymentAmt := mts.setupSendPaymentCase() | ||
|
||
// First, test that Alice's payment to Bob fails when the multipath |
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 comment needs to be updated.
itest/lnd_mpp_test.go
Outdated
|
||
success := ht.Run(test.name, func(t *testing.T) { | ||
// Bob creates an invoice for the payment. | ||
payReqs, _, _ := ht.CreatePayReqs(mts.bob, paymentAmt, |
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 we can instead create a single invoice for the whole test case as we know only the last send payment will succeed.
itest/lnd_mpp_test.go
Outdated
require.Equal(ht, hex.EncodeToString(invoices[0].RPreimage), | ||
payment.PaymentPreimage, "preimage doesn't match") | ||
|
||
// Verify that Bob records the invoice as settled for the full amount. |
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.
Can use ht.AssertInvoiceSettled
instead.
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 we should just add a unit-test instead of adding a itest.
Moreover let's remove the minRequired parts check yy has a valid point.
lnrpc/routerrpc/router_backend.go
Outdated
@@ -36,6 +36,10 @@ const ( | |||
// TODO(roasbeef): make this value dynamic based on expected number of | |||
// attempts for given amount. | |||
DefaultMaxParts = 16 | |||
|
|||
// MaxPartsUpperLimit defines the maximum allowable number of splits | |||
// for MPP when the user is attempting to send a payment. |
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.
it's MPP/AMP
itest/lnd_mpp_test.go
Outdated
// fails with the expected error when the payment amount exceeds the allowed | ||
// maximum, and that when the multipath parameters permit the full payment | ||
// amount, the payment is successfully settled. | ||
func testValidateMPPParams(ht *lntest.HarnessTest) { |
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.
Personally I think a itest for this change might be a bit overkill, can we not just test the extractIntentFromSendRequest
via a unit-test in router_backend_test.go
?
791dd97
to
7ae2f39
Compare
7ae2f39
to
314b7b1
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.
Nice, left some comments.
maxPartsInput uint32 | ||
shardAmountMsat uint64 |
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.
Nit: Why not keep the naming here from the above code ?
maxShardAmt, maxParts
?
valid bool | ||
expectedErrorMsg string | ||
} | ||
|
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.
Missing godoc, would be great if you could properly test the whole extract func. otherwise create a comment which specifies that you are in specific only testing the shard multipart payment logic.
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 have added tests for the entire extract function.
} | ||
|
||
for _, testCase := range testCases { | ||
testCase := testCase |
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 this iis not necessary anymore with the go compiler option loopvar
for _, testCase := range testCases { | ||
testCase := testCase | ||
t.Run(testCase.name, func(t *testing.T) { | ||
runExtractIntentTest(t, backend, testCase) |
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.
add t.Parallel()
to speed up testing.
314b7b1
to
49cd630
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.
Nice test!
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.
Well done 👍
Left some comments that might help make the test a bit cleaner.
@@ -475,3 +476,348 @@ func testUnmarshalAMP(t *testing.T, test unmarshalAMPTest) { | |||
t.Fatalf("test case has non-standard outcome") | |||
} | |||
} | |||
|
|||
type ExtractIntentTestCase struct { |
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.
nit: this struct doesn't need to be exported, so it can start with a lowercase letter.
backend: &RouterBackend{ | ||
MaxTotalTimelock: 1000, | ||
}, |
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.
nit: Setting MaxTotalTimelock is not needed.
backend: &RouterBackend{ | ||
MaxTotalTimelock: 1000, | ||
}, |
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.
nit: Setting MaxTotalTimelock
is not needed.
backend: &RouterBackend{ | ||
MaxTotalTimelock: 1000, | ||
}, |
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.
nit: Setting MaxTotalTimelock
is not needed.
backend: &RouterBackend{ | ||
MaxTotalTimelock: 1000, | ||
}, |
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.
nit: Setting MaxTotalTimelock
is not needed.
1adc9fd
to
e92ae2a
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.
Looking closer, I found some missing tests and suggest a few naming changes to make it clearer what each test is actually doing.
Besides the suggested additions I made, we could also add a test for the case where the invoice has an amount specified and the payer is not allowed to override it. However, to test that properly, we would need a non-expired invoice. I don't think it's strictly necessary, though.
"outgoing_chan_ids are mutually exclusive", | ||
}, | ||
{ | ||
name: "Invalid last hop pubkey", |
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.
nit: I would change the name of the test to "Invalid last hop pubkey length".
name: "Invalid last hop pubkey", | ||
backend: &RouterBackend{}, | ||
sendReq: &SendPaymentRequest{ | ||
OutgoingChanId: 38484, |
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.
nit: Setting OutgoingChanId
is not needed.
"mutually exclusive", | ||
}, | ||
{ | ||
name: "Dest custom records with type below minimum", |
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.
nit: I would change the name of the test to "Dest custom records with type below minimum range"
name: "Custom record entry with TLV type below " + | ||
"minimum", |
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.
nit: I would change the name of the test to "Custom record entry with TLV type below minimum range"
"payment_request cannot appear together", | ||
}, | ||
{ | ||
name: "Invalid payment request", |
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.
nit: I would change the name of the test to "Invalid payment request length"
"support AMP payments", | ||
}, | ||
{ | ||
name: "Invalid payment hash", |
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.
nit: I would change the name of the test to "Invalid payment hash length"
}, | ||
valid: false, | ||
expectedErrorMsg: "invalid vertex length", | ||
}, |
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.
You can add the test below to increase the code coverage:
{
name: "total time lock exceeds max allowed",
backend: &RouterBackend{
MaxTotalTimelock: 1000,
},
sendReq: &SendPaymentRequest{
CltvLimit: 1001,
},
valid: false,
expectedErrorMsg: "total time lock of 1001 exceeds "+
"max allowed 1000",
},
valid: false, | ||
expectedErrorMsg: "sat and msat arguments are " + | ||
"mutually exclusive", | ||
}, |
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.
You can add the test below to increase the code coverage:
{
name: "Fee limit cannot be negative",
backend: &RouterBackend{},
sendReq: &SendPaymentRequest{
FeeLimitSat: -1,
},
valid: false,
expectedErrorMsg: "amount cannot be negative",
},
Yes, we require a non-expired invoice, which is why I was unable to add a test for it. Perhaps, in such cases, "itest" would suffice? |
e92ae2a
to
f659db7
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.
LGTM
|
you can use: |
Adds validation to ensure that MPP parameters are compatible with the payment amount before attempting the payment. This prevents payments from entering a path finding loop that would eventually timeout. Signed-off-by: Nishant Bansal <[email protected]>
Signed-off-by: Nishant Bansal <[email protected]>
f659db7
to
8bd91d5
Compare
Thanks, this helped! I have also updated the tests for the latest changes. |
@ziggie1984: review reminder |
Change Description
Fixes: #8916
Adds validation to ensure that MPP parameters are compatible with the payment amount before attempting the payment. This prevents payments from entering a path finding loop that would eventually timeout.
Steps to Test
Added itest to validate that a payment with multipath parameters exceeding the allowed maximum amount fails with the expected error.
Pull Request Checklist
Testing
Code Style and Documentation
[skip ci]
in the commit message for small changes.📝 Please see our Contribution Guidelines for further guidance.