Skip to content

Commit 383a6d2

Browse files
committed
routing+channeldb: migrate MC store to use minimal Route encoding
Add a new mcRoute type that houses the data about a route that MC actually uses. Then add a migration (channeldb/migration32) that migrates the existing store from its current serialisation to the new, more minimal serialisation.
1 parent 96445f9 commit 383a6d2

File tree

9 files changed

+873
-136
lines changed

9 files changed

+873
-136
lines changed

channeldb/db.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/lightningnetwork/lnd/channeldb/migration29"
2727
"github.com/lightningnetwork/lnd/channeldb/migration30"
2828
"github.com/lightningnetwork/lnd/channeldb/migration31"
29+
"github.com/lightningnetwork/lnd/channeldb/migration32"
2930
"github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
3031
"github.com/lightningnetwork/lnd/clock"
3132
"github.com/lightningnetwork/lnd/invoices"
@@ -286,6 +287,10 @@ var (
286287
number: 31,
287288
migration: migration31.DeleteLastPublishedTxTLB,
288289
},
290+
{
291+
number: 32,
292+
migration: migration32.MigrateMCRouteSerialisation,
293+
},
289294
}
290295

291296
// optionalVersions stores all optional migrations that are applied

channeldb/migration32/migration.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package migration32
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
7+
"github.com/lightningnetwork/lnd/kvdb"
8+
)
9+
10+
// MigrateMCRouteSerialisation reads all the mission control store entries and
11+
// re-serializes them using a minimal route serialisation so that only the parts
12+
// of the route that are actually required for mission control are persisted.
13+
func MigrateMCRouteSerialisation(tx kvdb.RwTx) error {
14+
log.Infof("Migrating Mission Control store to use a more minimal " +
15+
"encoding for routes")
16+
17+
resultBucket := tx.ReadWriteBucket(resultsKey)
18+
19+
// If the results bucket does not exist then there are no entries in
20+
// the mission control store yet and so there is nothing to migrate.
21+
if resultBucket == nil {
22+
return nil
23+
}
24+
25+
// For each entry, read it into memory using the old encoding. Then,
26+
// extract the more minimal route, re-encode and persist the entry.
27+
return resultBucket.ForEach(func(k, v []byte) error {
28+
// Read the entry using the old encoding.
29+
resultOld, err := deserializeOldResult(k, v)
30+
if err != nil {
31+
return err
32+
}
33+
34+
// Convert to the new payment result format with the minimal
35+
// route.
36+
resultNew := convertPaymentResult(resultOld)
37+
38+
// Serialise the new payment result using the new encoding.
39+
key, resultNewBytes, err := serializeNewResult(resultNew)
40+
if err != nil {
41+
return err
42+
}
43+
44+
// Make sure that the derived key is the same.
45+
if !bytes.Equal(key, k) {
46+
return fmt.Errorf("new payment result key (%v) is "+
47+
"not the same as the old key (%v)", key, k)
48+
}
49+
50+
// Finally, overwrite the previous value with the new encoding.
51+
return resultBucket.Put(k, resultNewBytes)
52+
})
53+
}
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
package migration32
2+
3+
import (
4+
"encoding/hex"
5+
"testing"
6+
"time"
7+
8+
"github.com/btcsuite/btcd/btcec/v2"
9+
lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
10+
"github.com/lightningnetwork/lnd/channeldb/migtest"
11+
"github.com/lightningnetwork/lnd/kvdb"
12+
)
13+
14+
var (
15+
failureIndex = 8
16+
testPub = Vertex{2, 202, 4}
17+
testPub2 = Vertex{22, 202, 4}
18+
19+
pubkeyBytes, _ = hex.DecodeString(
20+
"598ec453728e0ffe0ae2f5e174243cf58f2" +
21+
"a3f2c83d2457b43036db568b11093",
22+
)
23+
pubKeyY = new(btcec.FieldVal)
24+
_ = pubKeyY.SetByteSlice(pubkeyBytes)
25+
pubkey = btcec.NewPublicKey(new(btcec.FieldVal).SetInt(4), pubKeyY)
26+
27+
paymentResultCommon1 = paymentResultCommon{
28+
id: 0,
29+
timeFwd: time.Unix(0, 1),
30+
timeReply: time.Unix(0, 2),
31+
success: false,
32+
failureSourceIdx: &failureIndex,
33+
failure: &lnwire.FailFeeInsufficient{},
34+
}
35+
36+
paymentResultCommon2 = paymentResultCommon{
37+
id: 2,
38+
timeFwd: time.Unix(0, 4),
39+
timeReply: time.Unix(0, 7),
40+
success: true,
41+
}
42+
)
43+
44+
// TestMigrateMCRouteSerialisation tests that the MigrateMCRouteSerialisation
45+
// migration function correctly migrates the MC store from using the old route
46+
// encoding to using the newer, more minimal route encoding.
47+
func TestMigrateMCRouteSerialisation(t *testing.T) {
48+
customRecord := map[uint64][]byte{
49+
65536: {4, 2, 2},
50+
}
51+
52+
resultsOld := []*paymentResultOld{
53+
{
54+
paymentResultCommon: paymentResultCommon1,
55+
route: &Route{
56+
TotalTimeLock: 100,
57+
TotalAmount: 400,
58+
SourcePubKey: testPub,
59+
Hops: []*Hop{
60+
// A hop with MPP, AMP and custom
61+
// records.
62+
{
63+
PubKeyBytes: testPub,
64+
ChannelID: 100,
65+
OutgoingTimeLock: 300,
66+
AmtToForward: 500,
67+
MPP: &MPP{
68+
paymentAddr: [32]byte{
69+
4, 5,
70+
},
71+
totalMsat: 900,
72+
},
73+
AMP: &AMP{
74+
rootShare: [32]byte{
75+
0, 0,
76+
},
77+
setID: [32]byte{
78+
5, 5, 5,
79+
},
80+
childIndex: 90,
81+
},
82+
CustomRecords: customRecord,
83+
Metadata: []byte{6, 7, 7},
84+
},
85+
// A legacy hop.
86+
{
87+
PubKeyBytes: testPub,
88+
ChannelID: 800,
89+
OutgoingTimeLock: 4,
90+
AmtToForward: 4,
91+
LegacyPayload: true,
92+
},
93+
// A hop with a blinding key.
94+
{
95+
PubKeyBytes: testPub,
96+
ChannelID: 800,
97+
OutgoingTimeLock: 4,
98+
AmtToForward: 4,
99+
BlindingPoint: pubkey,
100+
EncryptedData: []byte{
101+
1, 2, 3,
102+
},
103+
TotalAmtMsat: 600,
104+
},
105+
// A hop with a blinding key and custom
106+
// records.
107+
{
108+
PubKeyBytes: testPub,
109+
ChannelID: 800,
110+
OutgoingTimeLock: 4,
111+
AmtToForward: 4,
112+
CustomRecords: customRecord,
113+
BlindingPoint: pubkey,
114+
EncryptedData: []byte{
115+
1, 2, 3,
116+
},
117+
TotalAmtMsat: 600,
118+
},
119+
},
120+
},
121+
},
122+
{
123+
paymentResultCommon: paymentResultCommon2,
124+
route: &Route{
125+
TotalTimeLock: 101,
126+
TotalAmount: 401,
127+
SourcePubKey: testPub2,
128+
Hops: []*Hop{
129+
{
130+
PubKeyBytes: testPub,
131+
ChannelID: 800,
132+
OutgoingTimeLock: 4,
133+
AmtToForward: 4,
134+
BlindingPoint: pubkey,
135+
EncryptedData: []byte{
136+
1, 2, 3,
137+
},
138+
TotalAmtMsat: 600,
139+
},
140+
},
141+
},
142+
},
143+
}
144+
145+
expectedResultsNew := []*paymentResultNew{
146+
{
147+
paymentResultCommon: paymentResultCommon1,
148+
route: &mcRoute{
149+
sourcePubKey: testPub,
150+
totalAmount: 400,
151+
hops: []*mcHop{
152+
{
153+
channelID: 100,
154+
pubKeyBytes: testPub,
155+
amtToFwd: 500,
156+
hasCustomRecords: true,
157+
},
158+
{
159+
channelID: 800,
160+
pubKeyBytes: testPub,
161+
amtToFwd: 4,
162+
},
163+
{
164+
channelID: 800,
165+
pubKeyBytes: testPub,
166+
amtToFwd: 4,
167+
hasBlindingPoint: true,
168+
},
169+
{
170+
channelID: 800,
171+
pubKeyBytes: testPub,
172+
amtToFwd: 4,
173+
hasBlindingPoint: true,
174+
hasCustomRecords: true,
175+
},
176+
},
177+
},
178+
},
179+
{
180+
paymentResultCommon: paymentResultCommon2,
181+
route: &mcRoute{
182+
sourcePubKey: testPub2,
183+
totalAmount: 401,
184+
hops: []*mcHop{
185+
{
186+
channelID: 800,
187+
pubKeyBytes: testPub,
188+
amtToFwd: 4,
189+
hasBlindingPoint: true,
190+
},
191+
},
192+
},
193+
},
194+
}
195+
196+
// Prime the database with some mission control data that uses the
197+
// old route encoding.
198+
before := func(tx kvdb.RwTx) error {
199+
resultBucket, err := tx.CreateTopLevelBucket(resultsKey)
200+
if err != nil {
201+
return err
202+
}
203+
204+
for _, result := range resultsOld {
205+
k, v, err := serializeOldResult(result)
206+
if err != nil {
207+
return err
208+
}
209+
210+
if err := resultBucket.Put(k, v); err != nil {
211+
return err
212+
}
213+
}
214+
215+
return nil
216+
}
217+
218+
// After the migration, ensure that all the relevant info was
219+
// maintained.
220+
after := func(tx kvdb.RwTx) error {
221+
m := make(map[string]interface{})
222+
for _, result := range expectedResultsNew {
223+
k, v, err := serializeNewResult(result)
224+
if err != nil {
225+
return err
226+
}
227+
228+
m[string(k)] = string(v)
229+
}
230+
231+
return migtest.VerifyDB(tx, resultsKey, m)
232+
}
233+
234+
migtest.ApplyMigration(
235+
t, before, after, MigrateMCRouteSerialisation, false,
236+
)
237+
}

0 commit comments

Comments
 (0)