mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-27 21:36:17 +02:00
itest: enhance mission control namespace itest
In this commit, we enhance the itess to exercise the new query+reset+import namespace features. This also rounds out the test as well to make sure that the mc payment portion works properly.
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
package itest
|
package itest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||||
@@ -11,79 +14,187 @@ import (
|
|||||||
// testMissionControlNamespace tests that payments can use different mission
|
// testMissionControlNamespace tests that payments can use different mission
|
||||||
// control namespaces to maintain separate routing histories.
|
// control namespaces to maintain separate routing histories.
|
||||||
func testMissionControlNamespace(ht *lntest.HarnessTest) {
|
func testMissionControlNamespace(ht *lntest.HarnessTest) {
|
||||||
// Create a simple two-node network.
|
|
||||||
const chanAmt = btcutil.Amount(1_000_000)
|
const chanAmt = btcutil.Amount(1_000_000)
|
||||||
|
|
||||||
// Create two nodes. Alice gets funded.
|
// We'll create a simple two node network for this tesat.
|
||||||
alice := ht.NewNodeWithCoins("Alice", nil)
|
alice := ht.NewNodeWithCoins("Alice", nil)
|
||||||
bob := ht.NewNode("Bob", nil)
|
bob := ht.NewNode("Bob", nil)
|
||||||
|
|
||||||
// Connect nodes.
|
|
||||||
ht.EnsureConnected(alice, bob)
|
ht.EnsureConnected(alice, bob)
|
||||||
|
|
||||||
// Open channel: Alice -> Bob.
|
|
||||||
chanPoint := ht.OpenChannel(
|
chanPoint := ht.OpenChannel(
|
||||||
alice, bob, lntest.OpenChannelParams{
|
alice, bob, lntest.OpenChannelParams{
|
||||||
Amt: chanAmt,
|
Amt: chanAmt,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// Wait for channel to be seen by both nodes.
|
|
||||||
ht.AssertChannelInGraph(alice, chanPoint)
|
ht.AssertChannelInGraph(alice, chanPoint)
|
||||||
ht.AssertChannelInGraph(bob, chanPoint)
|
ht.AssertChannelInGraph(bob, chanPoint)
|
||||||
|
|
||||||
// Create an invoice from Bob.
|
// Create an invoice for Bob that we'll pay below.
|
||||||
const paymentAmt = 10_000
|
const paymentAmt = 10_000
|
||||||
invoice := bob.RPC.AddInvoice(&lnrpc.Invoice{
|
invoice := bob.RPC.AddInvoice(&lnrpc.Invoice{
|
||||||
Value: paymentAmt,
|
Value: paymentAmt,
|
||||||
Memo: "test payment default namespace",
|
Memo: "test payment default namespace",
|
||||||
})
|
})
|
||||||
|
|
||||||
// Reset mission control to ensure clean state.
|
// Reset mission control for the default namespace to ensure clean
|
||||||
alice.RPC.ResetMissionControl()
|
// state.
|
||||||
|
_, err := alice.RPC.Router.ResetMissionControl(
|
||||||
|
ht.Context(), &routerrpc.ResetMissionControlRequest{
|
||||||
|
MissionControlNamespace: "",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
require.NoError(ht.T, err, "failed to reset default mission control")
|
||||||
|
|
||||||
// Send a payment using the default namespace.
|
ht.Run("send_payment_with_namespaces", func(t *testing.T) {
|
||||||
req := &routerrpc.SendPaymentRequest{
|
// Send a payment using the default namespace.
|
||||||
PaymentRequest: invoice.PaymentRequest,
|
reqDefault := &routerrpc.SendPaymentRequest{
|
||||||
TimeoutSeconds: 60,
|
PaymentRequest: invoice.PaymentRequest,
|
||||||
MissionControlNamespace: "", // default namespace
|
TimeoutSeconds: 60,
|
||||||
}
|
MissionControlNamespace: "",
|
||||||
ht.SendPaymentAssertSettled(alice, req)
|
}
|
||||||
|
ht.SendPaymentAssertSettled(alice, reqDefault)
|
||||||
|
|
||||||
// Create another invoice.
|
// Make another payment with Bob but use our custom namespace
|
||||||
invoice2 := bob.RPC.AddInvoice(&lnrpc.Invoice{
|
// this time instead.
|
||||||
Value: paymentAmt,
|
invoice2 := bob.RPC.AddInvoice(&lnrpc.Invoice{
|
||||||
Memo: "test payment custom namespace",
|
Value: paymentAmt,
|
||||||
|
Memo: "test payment custom namespace",
|
||||||
|
})
|
||||||
|
customNs := "custom-namespace-send"
|
||||||
|
reqCustom := &routerrpc.SendPaymentRequest{
|
||||||
|
PaymentRequest: invoice2.PaymentRequest,
|
||||||
|
TimeoutSeconds: 60,
|
||||||
|
MissionControlNamespace: customNs,
|
||||||
|
}
|
||||||
|
payment := ht.SendPaymentAssertSettled(alice, reqCustom)
|
||||||
|
require.Equal(t, lnrpc.Payment_SUCCEEDED, payment.Status,
|
||||||
|
"payment with custom namespace should succeed")
|
||||||
|
|
||||||
|
// If we query for the default mc, then we should find that the
|
||||||
|
// records are populated.
|
||||||
|
mcReqDefault := &routerrpc.QueryMissionControlRequest{
|
||||||
|
MissionControlNamespace: "",
|
||||||
|
}
|
||||||
|
defaultMC, err := alice.RPC.Router.QueryMissionControl(
|
||||||
|
ht.Context(), mcReqDefault,
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, defaultMC.Pairs,
|
||||||
|
"default namespace should have recorded channel usage")
|
||||||
|
|
||||||
|
// Similarly, we should find that the custom namespace also has
|
||||||
|
// entries.
|
||||||
|
mcReqCustom := &routerrpc.QueryMissionControlRequest{
|
||||||
|
MissionControlNamespace: customNs,
|
||||||
|
}
|
||||||
|
customMC, err := alice.RPC.Router.QueryMissionControl(
|
||||||
|
ht.Context(), mcReqCustom,
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, customMC.Pairs,
|
||||||
|
"custom namespace should have recorded channel usage")
|
||||||
|
|
||||||
|
require.True(t, len(defaultMC.Pairs) > 0, "Default MC empty")
|
||||||
|
require.True(t, len(customMC.Pairs) > 0, "Custom MC empty")
|
||||||
})
|
})
|
||||||
|
|
||||||
// Send a payment using a custom namespace.
|
ht.Run("reset_mission_control_with_namespace", func(t *testing.T) {
|
||||||
customReq := &routerrpc.SendPaymentRequest{
|
customNsReset := "namespace-to-reset"
|
||||||
PaymentRequest: invoice2.PaymentRequest,
|
|
||||||
TimeoutSeconds: 60,
|
|
||||||
MissionControlNamespace: "custom-namespace",
|
|
||||||
}
|
|
||||||
payment := ht.SendPaymentAssertSettled(alice, customReq)
|
|
||||||
require.Equal(ht.T, lnrpc.Payment_SUCCEEDED, payment.Status,
|
|
||||||
"payment with custom namespace should succeed")
|
|
||||||
|
|
||||||
// Query mission control to verify the default namespace has recorded
|
// Send a payment to populate this new custom namespace.
|
||||||
// the payment attempt.
|
invReset := bob.RPC.AddInvoice(&lnrpc.Invoice{Value: 100})
|
||||||
mcReq := &routerrpc.QueryMissionControlRequest{}
|
reqPopulate := &routerrpc.SendPaymentRequest{
|
||||||
defaultMC, err := alice.RPC.Router.QueryMissionControl(
|
PaymentRequest: invReset.PaymentRequest,
|
||||||
ht.Context(), mcReq,
|
TimeoutSeconds: 60,
|
||||||
)
|
MissionControlNamespace: customNsReset,
|
||||||
require.NoError(ht.T, err)
|
}
|
||||||
|
ht.SendPaymentAssertSettled(alice, reqPopulate)
|
||||||
// The default namespace should have recorded at least one pair.
|
|
||||||
require.NotEmpty(ht.T, defaultMC.Pairs,
|
|
||||||
"default namespace should have recorded channel usage")
|
|
||||||
|
|
||||||
// The test passes if we have any pairs recorded, which proves
|
|
||||||
// mission control is working with the default namespace.
|
|
||||||
// The custom namespace payment also succeeded, demonstrating
|
|
||||||
// namespace isolation.
|
|
||||||
|
|
||||||
// Note: Currently there's no way to query a specific namespace's
|
// Query the namespace, we should find now that it has some
|
||||||
// mission control state via RPC, but the fact that both payments
|
// entries populated.
|
||||||
// succeeded proves the namespace isolation is working correctly.
|
mcBeforeReset, err := alice.RPC.Router.QueryMissionControl(
|
||||||
}
|
ht.Context(), &routerrpc.QueryMissionControlRequest{
|
||||||
|
MissionControlNamespace: customNsReset,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(
|
||||||
|
t, mcBeforeReset.Pairs, "MC should be populated "+
|
||||||
|
"before reset",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Now, we'll reset the ns, then check below that it's actually
|
||||||
|
// empty.
|
||||||
|
_, errReset := alice.RPC.Router.ResetMissionControl(
|
||||||
|
ht.Context(), &routerrpc.ResetMissionControlRequest{
|
||||||
|
MissionControlNamespace: customNsReset,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
require.NoError(t, errReset)
|
||||||
|
|
||||||
|
// Query again to confirm it's empty.
|
||||||
|
mcAfterReset, err := alice.RPC.Router.QueryMissionControl(
|
||||||
|
ht.Context(), &routerrpc.QueryMissionControlRequest{
|
||||||
|
MissionControlNamespace: customNsReset,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(
|
||||||
|
t, mcAfterReset.Pairs, "MC should be empty after reset",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Now we'll test isolation: the default namespace shouldn't
|
||||||
|
// have been affected.
|
||||||
|
defaultMC, err := alice.RPC.Router.QueryMissionControl(
|
||||||
|
ht.Context(), &routerrpc.QueryMissionControlRequest{
|
||||||
|
MissionControlNamespace: "",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(
|
||||||
|
t, defaultMC.Pairs, "Default MC should not be empty",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
ht.Run("ximport_mission_control_with_namespace", func(t *testing.T) {
|
||||||
|
customNs := "namespace-to-import"
|
||||||
|
|
||||||
|
// We'll make some fake data to import as weights.
|
||||||
|
importData := &routerrpc.XImportMissionControlRequest{ //nolint:ll
|
||||||
|
MissionControlNamespace: customNs,
|
||||||
|
Pairs: []*routerrpc.PairHistory{
|
||||||
|
{
|
||||||
|
NodeFrom: alice.PubKey[:],
|
||||||
|
NodeTo: bob.PubKey[:],
|
||||||
|
History: &routerrpc.PairData{
|
||||||
|
FailTime: time.Now().Unix() - 1000,
|
||||||
|
FailAmtMsat: 5000,
|
||||||
|
SuccessTime: time.Now().Unix(),
|
||||||
|
SuccessAmtMsat: 10000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Force: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := alice.RPC.Router.XImportMissionControl(
|
||||||
|
ht.Context(), importData,
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// That data that we created above should be found now.
|
||||||
|
mcAfterImport, err := alice.RPC.Router.QueryMissionControl(
|
||||||
|
ht.Context(), &routerrpc.QueryMissionControlRequest{
|
||||||
|
MissionControlNamespace: customNs,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(
|
||||||
|
t, mcAfterImport.Pairs, "MC should have imported data",
|
||||||
|
)
|
||||||
|
require.Equal(
|
||||||
|
t, int64(10000),
|
||||||
|
mcAfterImport.Pairs[0].History.SuccessAmtMsat,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user