mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-27 07:46:45 +01:00
115 lines
3.4 KiB
Go
115 lines
3.4 KiB
Go
|
package migration6
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"errors"
|
||
|
|
||
|
"github.com/lightningnetwork/lnd/kvdb"
|
||
|
"github.com/lightningnetwork/lnd/tlv"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// cSessionBkt is a top-level bucket storing:
|
||
|
// session-id => cSessionBody -> encoded ClientSessionBody
|
||
|
// => cSessionDBID -> db-assigned-id
|
||
|
// => cSessionCommits => seqnum -> encoded CommittedUpdate
|
||
|
// => cSessionAcks => seqnum -> encoded BackupID
|
||
|
cSessionBkt = []byte("client-session-bucket")
|
||
|
|
||
|
// cSessionDBID is a key used in the cSessionBkt to store the
|
||
|
// db-assigned-id of a session.
|
||
|
cSessionDBID = []byte("client-session-db-id")
|
||
|
|
||
|
// cSessionIDIndexBkt is a top-level bucket storing:
|
||
|
// db-assigned-id -> session-id
|
||
|
cSessionIDIndexBkt = []byte("client-session-id-index")
|
||
|
|
||
|
// cSessionBody is a sub-bucket of cSessionBkt storing only the body of
|
||
|
// the ClientSession.
|
||
|
cSessionBody = []byte("client-session-body")
|
||
|
|
||
|
// ErrUninitializedDB signals that top-level buckets for the database
|
||
|
// have not been initialized.
|
||
|
ErrUninitializedDB = errors.New("db not initialized")
|
||
|
|
||
|
// ErrCorruptClientSession signals that the client session's on-disk
|
||
|
// structure deviates from what is expected.
|
||
|
ErrCorruptClientSession = errors.New("client session corrupted")
|
||
|
|
||
|
byteOrder = binary.BigEndian
|
||
|
)
|
||
|
|
||
|
// MigrateSessionIDIndex adds a new session ID index to the tower client db.
|
||
|
// This index is a mapping from db-assigned ID (a uint64 encoded using BigSize)
|
||
|
// to real session ID (33 bytes). This mapping will allow us to persist session
|
||
|
// pointers with fewer bytes in the future.
|
||
|
func MigrateSessionIDIndex(tx kvdb.RwTx) error {
|
||
|
log.Infof("Migrating the tower client db to add a new session ID " +
|
||
|
"index which stores a mapping from db-assigned ID to real " +
|
||
|
"session ID")
|
||
|
|
||
|
// Create a new top-level bucket for the index.
|
||
|
indexBkt, err := tx.CreateTopLevelBucket(cSessionIDIndexBkt)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Get the existing top-level sessions bucket.
|
||
|
sessionsBkt := tx.ReadWriteBucket(cSessionBkt)
|
||
|
if sessionsBkt == nil {
|
||
|
return ErrUninitializedDB
|
||
|
}
|
||
|
|
||
|
// Iterate over the sessions bucket where each key is a session-ID.
|
||
|
return sessionsBkt.ForEach(func(sessionID, _ []byte) error {
|
||
|
// Ask the DB for a new, unique, id for the index bucket.
|
||
|
nextSeq, err := indexBkt.NextSequence()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
newIndex, err := writeBigSize(nextSeq)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Add the new db-assigned-ID to real-session-ID pair to the
|
||
|
// new index bucket.
|
||
|
err = indexBkt.Put(newIndex, sessionID)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Get the sub-bucket for this specific session ID.
|
||
|
sessionBkt := sessionsBkt.NestedReadWriteBucket(sessionID)
|
||
|
if sessionBkt == nil {
|
||
|
return ErrCorruptClientSession
|
||
|
}
|
||
|
|
||
|
// Here we ensure that the session bucket includes a session
|
||
|
// body. The only reason we do this is so that we can simulate
|
||
|
// a migration fail in a test to ensure that a migration fail
|
||
|
// results in an untouched db.
|
||
|
sessionBodyBytes := sessionBkt.Get(cSessionBody)
|
||
|
if sessionBodyBytes == nil {
|
||
|
return ErrCorruptClientSession
|
||
|
}
|
||
|
|
||
|
// Add the db-assigned ID of the session to the session under
|
||
|
// the cSessionDBID key.
|
||
|
return sessionBkt.Put(cSessionDBID, newIndex)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// writeBigSize will encode the given uint64 as a BigSize byte slice.
|
||
|
func writeBigSize(i uint64) ([]byte, error) {
|
||
|
var b bytes.Buffer
|
||
|
err := tlv.WriteVarInt(&b, i, &[8]byte{})
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return b.Bytes(), nil
|
||
|
}
|