lnd/channeldb/migration13/migration.go
2021-05-07 14:18:56 +02:00

203 lines
5.3 KiB
Go

package migration13
import (
"encoding/binary"
"fmt"
"github.com/lightningnetwork/lnd/kvdb"
)
var (
paymentsRootBucket = []byte("payments-root-bucket")
// paymentCreationInfoKey is a key used in the payment's sub-bucket to
// store the creation info of the payment.
paymentCreationInfoKey = []byte("payment-creation-info")
// paymentFailInfoKey is a key used in the payment's sub-bucket to
// store information about the reason a payment failed.
paymentFailInfoKey = []byte("payment-fail-info")
// paymentAttemptInfoKey is a key used in the payment's sub-bucket to
// store the info about the latest attempt that was done for the
// payment in question.
paymentAttemptInfoKey = []byte("payment-attempt-info")
// paymentSettleInfoKey is a key used in the payment's sub-bucket to
// store the settle info of the payment.
paymentSettleInfoKey = []byte("payment-settle-info")
// paymentHtlcsBucket is a bucket where we'll store the information
// about the HTLCs that were attempted for a payment.
paymentHtlcsBucket = []byte("payment-htlcs-bucket")
// htlcAttemptInfoKey is a key used in a HTLC's sub-bucket to store the
// info about the attempt that was done for the HTLC in question.
htlcAttemptInfoKey = []byte("htlc-attempt-info")
// htlcSettleInfoKey is a key used in a HTLC's sub-bucket to store the
// settle info, if any.
htlcSettleInfoKey = []byte("htlc-settle-info")
// htlcFailInfoKey is a key used in a HTLC's sub-bucket to store
// failure information, if any.
htlcFailInfoKey = []byte("htlc-fail-info")
byteOrder = binary.BigEndian
)
// MigrateMPP migrates the payments to a new structure that accommodates for mpp
// payments.
func MigrateMPP(tx kvdb.RwTx) error {
log.Infof("Migrating payments to mpp structure")
// Iterate over all payments and store their indexing keys. This is
// needed, because no modifications are allowed inside a Bucket.ForEach
// loop.
paymentsBucket := tx.ReadWriteBucket(paymentsRootBucket)
if paymentsBucket == nil {
return nil
}
var paymentKeys [][]byte
err := paymentsBucket.ForEach(func(k, v []byte) error {
paymentKeys = append(paymentKeys, k)
return nil
})
if err != nil {
return err
}
// With all keys retrieved, start the migration.
for _, k := range paymentKeys {
bucket := paymentsBucket.NestedReadWriteBucket(k)
// We only expect sub-buckets to be found in
// this top-level bucket.
if bucket == nil {
return fmt.Errorf("non bucket element in " +
"payments bucket")
}
// Fetch old format creation info.
creationInfo := bucket.Get(paymentCreationInfoKey)
if creationInfo == nil {
return fmt.Errorf("creation info not found")
}
// Make a copy because bbolt doesn't allow this value to be
// changed in-place.
newCreationInfo := make([]byte, len(creationInfo))
copy(newCreationInfo, creationInfo)
// Convert to nano seconds.
timeBytes := newCreationInfo[32+8 : 32+8+8]
time := byteOrder.Uint64(timeBytes)
timeNs := time * 1000000000
byteOrder.PutUint64(timeBytes, timeNs)
// Write back new format creation info.
err := bucket.Put(paymentCreationInfoKey, newCreationInfo)
if err != nil {
return err
}
// No migration needed if there is no attempt stored.
attemptInfo := bucket.Get(paymentAttemptInfoKey)
if attemptInfo == nil {
continue
}
// Delete attempt info on the payment level.
if err := bucket.Delete(paymentAttemptInfoKey); err != nil {
return err
}
// Save attempt id for later use.
attemptID := attemptInfo[:8]
// Discard attempt id. It will become a bucket key in the new
// structure.
attemptInfo = attemptInfo[8:]
// Append unknown (zero) attempt time.
var zero [8]byte
attemptInfo = append(attemptInfo, zero[:]...)
// Create bucket that contains all htlcs.
htlcsBucket, err := bucket.CreateBucket(paymentHtlcsBucket)
if err != nil {
return err
}
// Create an htlc for this attempt.
htlcBucket, err := htlcsBucket.CreateBucket(attemptID)
if err != nil {
return err
}
// Save migrated attempt info.
err = htlcBucket.Put(htlcAttemptInfoKey, attemptInfo)
if err != nil {
return err
}
// Migrate settle info.
settleInfo := bucket.Get(paymentSettleInfoKey)
if settleInfo != nil {
// Payment-level settle info can be deleted.
err := bucket.Delete(paymentSettleInfoKey)
if err != nil {
return err
}
// Append unknown (zero) settle time.
settleInfo = append(settleInfo, zero[:]...)
// Save settle info.
err = htlcBucket.Put(htlcSettleInfoKey, settleInfo)
if err != nil {
return err
}
// Migration for settled htlc completed.
continue
}
// If there is no payment-level failure reason, the payment is
// still in flight and nothing else needs to be migrated.
// Otherwise the payment-level failure reason can remain
// unchanged.
inFlight := bucket.Get(paymentFailInfoKey) == nil
if inFlight {
continue
}
// The htlc failed. Add htlc fail info with reason unknown. We
// don't have access to the original failure reason anymore.
failInfo := []byte{
// Fail time unknown.
0, 0, 0, 0, 0, 0, 0, 0,
// Zero length wire message.
0,
// Failure reason unknown.
0,
// Failure source index zero.
0, 0, 0, 0,
}
// Save fail info.
err = htlcBucket.Put(htlcFailInfoKey, failInfo)
if err != nil {
return err
}
}
log.Infof("Migration of payments to mpp structure complete!")
return nil
}