mirror of
https://github.com/lightninglabs/lightning-terminal.git
synced 2026-08-13 12:33:36 +02:00
session: migration to populate group ID to session ID indexes
This commit adds a migration to the session store to back-fill the new session ID to group ID and group ID to session IDs indexes.
This commit is contained in:
parent
a802759f4e
commit
e90df06d58
3 changed files with 243 additions and 0 deletions
|
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/lightninglabs/lightning-terminal/session/migration1"
|
||||
"github.com/lightninglabs/lightning-terminal/session/migration2"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
|
|
@ -37,6 +38,7 @@ var (
|
|||
tx, time.Now,
|
||||
)
|
||||
},
|
||||
migration2.MigrateSessionIDToGroupIndex,
|
||||
}
|
||||
|
||||
latestDBVersion = uint32(len(dbVersions))
|
||||
|
|
|
|||
119
session/migration2/id_to_group_index.go
Normal file
119
session/migration2/id_to_group_index.go
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
package migration2
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
var (
|
||||
// sessionBucketKey is the top level bucket where we can find all
|
||||
// information about sessions. These sessions are indexed by their
|
||||
// public key.
|
||||
//
|
||||
// The session bucket has the following structure:
|
||||
// session -> <key> -> <serialised session>
|
||||
// -> id-index -> <session-id> -> key -> <session key>
|
||||
// -> group -> <group-ID>
|
||||
// -> group-id-index -> <group-id> -> session-id -> sequence -> <session-id>
|
||||
sessionBucketKey = []byte("session")
|
||||
|
||||
// idIndexKey is the key used to define the id-index sub-bucket within
|
||||
// the main session bucket. This bucket will be used to store the
|
||||
// mapping from session ID to various other fields.
|
||||
idIndexKey = []byte("id-index")
|
||||
|
||||
// sessionKeyKey is the key used within the id-index bucket to store the
|
||||
// session key (serialised local public key) associated with the given
|
||||
// session ID.
|
||||
sessionKeyKey = []byte("key")
|
||||
|
||||
// groupIDKey is the key used within the id-index bucket to store the
|
||||
// group ID associated with the given session ID.
|
||||
groupIDKey = []byte("group")
|
||||
|
||||
// groupIDIndexKey is the key used to define the group-id-index
|
||||
// sub-bucket within the main session bucket. This bucket will be used
|
||||
// to store the mapping from group ID to various other fields.
|
||||
groupIDIndexKey = []byte("group-id-index")
|
||||
|
||||
// sessionIDKey is a key used in the group-id-index under a sub-bucket
|
||||
// defined by a specific group ID. It will be used to store the session
|
||||
// IDs associated with the given group ID.
|
||||
sessionIDKey = []byte("session-id")
|
||||
|
||||
// ErrDBInitErr is returned when a bucket that we expect to have been
|
||||
// set up during DB initialisation is not found.
|
||||
ErrDBInitErr = errors.New("db did not initialise properly")
|
||||
|
||||
// byteOrder is the default byte order we'll use for serialization
|
||||
// within the database.
|
||||
byteOrder = binary.BigEndian
|
||||
)
|
||||
|
||||
// MigrateSessionIDToGroupIndex back-fills the session ID to group index so that
|
||||
// it has an entry for all sessions that the session store is currently aware of.
|
||||
func MigrateSessionIDToGroupIndex(tx *bbolt.Tx) error {
|
||||
sessionBucket := tx.Bucket(sessionBucketKey)
|
||||
if sessionBucket == nil {
|
||||
return fmt.Errorf("session bucket not found")
|
||||
}
|
||||
|
||||
idIndexBkt := sessionBucket.Bucket(idIndexKey)
|
||||
if idIndexBkt == nil {
|
||||
return ErrDBInitErr
|
||||
}
|
||||
|
||||
groupIndexBkt := sessionBucket.Bucket(groupIDIndexKey)
|
||||
if groupIndexBkt == nil {
|
||||
return ErrDBInitErr
|
||||
}
|
||||
|
||||
// Collect all the index entries.
|
||||
return idIndexBkt.ForEach(func(sessionID, _ []byte) error {
|
||||
// This migration is done before the logic in LiT is added that
|
||||
// would allow groupIDs to differ from session IDs. And so all
|
||||
// this migration needs to do is add the current 1:1 mapping
|
||||
// from group ID to session ID and vice versa where group ID is
|
||||
// equal to the session ID.
|
||||
groupID := sessionID
|
||||
|
||||
// First we add the session ID to group ID mapping.
|
||||
sessionIDBkt := idIndexBkt.Bucket(sessionID)
|
||||
if sessionIDBkt == nil {
|
||||
return fmt.Errorf("unexpected non-bucket entry in " +
|
||||
"the id-index bucket")
|
||||
}
|
||||
|
||||
err := sessionIDBkt.Put(groupIDKey, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Now we will add the group ID to session ID mapping.
|
||||
groupIDBkt, err := groupIndexBkt.CreateBucketIfNotExists(
|
||||
groupID,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
groupSessionIDBkt, err := groupIDBkt.CreateBucketIfNotExists(
|
||||
sessionIDKey,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nextSeq, err := groupSessionIDBkt.NextSequence()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var seqNoBytes [8]byte
|
||||
byteOrder.PutUint64(seqNoBytes[:], nextSeq)
|
||||
|
||||
return groupSessionIDBkt.Put(seqNoBytes[:], groupID[:])
|
||||
})
|
||||
}
|
||||
122
session/migration2/id_to_group_index_test.go
Normal file
122
session/migration2/id_to_group_index_test.go
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package migration2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/lightninglabs/lightning-terminal/session/migtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
// ID represents the id of a session.
|
||||
type ID [4]byte
|
||||
|
||||
// TestMigrateSessionIDToGroupIDIndex tests that the
|
||||
// MigrateSessionIDToGroupIDIndex migration correctly back-fills the session ID
|
||||
// to group ID index along with the group ID to session ID index.
|
||||
func TestMigrateSessionIDToGroupIDIndex(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Make a few session IDs.
|
||||
sess1ID, sess1Key := newSessionID(t)
|
||||
sess2ID, sess2Key := newSessionID(t)
|
||||
sess3ID, sess3Key := newSessionID(t)
|
||||
|
||||
// Put together a sample session ID index DB based on the above.
|
||||
idIndexBefore := map[string]interface{}{
|
||||
string(sess1ID[:]): map[string]interface{}{
|
||||
string(sessionKeyKey): string(sess1Key),
|
||||
},
|
||||
string(sess2ID[:]): map[string]interface{}{
|
||||
string(sessionKeyKey): string(sess2Key),
|
||||
},
|
||||
string(sess3ID[:]): map[string]interface{}{
|
||||
string(sessionKeyKey): string(sess3Key),
|
||||
},
|
||||
}
|
||||
|
||||
// sessionDBBefore is what our session DB will look like before the
|
||||
// migration.
|
||||
sessionDBBefore := map[string]interface{}{
|
||||
string(idIndexKey): idIndexBefore,
|
||||
string(groupIDIndexKey): map[string]interface{}{},
|
||||
}
|
||||
|
||||
before := func(tx *bbolt.Tx) error {
|
||||
return migtest.RestoreDB(tx, sessionBucketKey, sessionDBBefore)
|
||||
}
|
||||
|
||||
// Put together what we expect the resulting id-index bucket to look
|
||||
// like after the migration.
|
||||
idIndexAfter := map[string]interface{}{
|
||||
string(sess1ID[:]): map[string]interface{}{
|
||||
string(sessionKeyKey): string(sess1Key),
|
||||
string(groupIDKey): string(sess1ID[:]),
|
||||
},
|
||||
string(sess2ID[:]): map[string]interface{}{
|
||||
string(sessionKeyKey): string(sess2Key),
|
||||
string(groupIDKey): string(sess2ID[:]),
|
||||
},
|
||||
string(sess3ID[:]): map[string]interface{}{
|
||||
string(sessionKeyKey): string(sess3Key),
|
||||
string(groupIDKey): string(sess3ID[:]),
|
||||
},
|
||||
}
|
||||
|
||||
// Put together what we expect the resulting group-ID-index bucket to
|
||||
// look like after the migration.
|
||||
groupIDIndexAfter := map[string]interface{}{
|
||||
string(sess1ID[:]): map[string]interface{}{
|
||||
string(sessionIDKey): map[string]interface{}{
|
||||
sequenceString(1): string(sess1ID[:]),
|
||||
},
|
||||
},
|
||||
string(sess2ID[:]): map[string]interface{}{
|
||||
string(sessionIDKey): map[string]interface{}{
|
||||
sequenceString(1): string(sess2ID[:]),
|
||||
},
|
||||
},
|
||||
string(sess3ID[:]): map[string]interface{}{
|
||||
string(sessionIDKey): map[string]interface{}{
|
||||
sequenceString(1): string(sess3ID[:]),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// sessionDBAfter is what our session DB will look like after the
|
||||
// migration.
|
||||
sessionDBAfter := map[string]interface{}{
|
||||
string(idIndexKey): idIndexAfter,
|
||||
string(groupIDIndexKey): groupIDIndexAfter,
|
||||
}
|
||||
|
||||
after := func(tx *bbolt.Tx) error {
|
||||
return migtest.VerifyDB(tx, sessionBucketKey, sessionDBAfter)
|
||||
}
|
||||
|
||||
migtest.ApplyMigration(
|
||||
t, before, after, MigrateSessionIDToGroupIndex, false,
|
||||
)
|
||||
}
|
||||
|
||||
// newSessionID is a helper function that can be used to generate a new session
|
||||
// ID and key.
|
||||
func newSessionID(t *testing.T) (ID, []byte) {
|
||||
privateKey, err := btcec.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
key := privateKey.PubKey().SerializeCompressed()
|
||||
|
||||
var id ID
|
||||
copy(id[:], key)
|
||||
|
||||
return id, key
|
||||
}
|
||||
|
||||
func sequenceString(id uint64) string {
|
||||
var seqNoBytes [8]byte
|
||||
byteOrder.PutUint64(seqNoBytes[:], id)
|
||||
|
||||
return string(seqNoBytes[:])
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue