mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
164 lines
4.9 KiB
Go
164 lines
4.9 KiB
Go
package migration23
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/lightningnetwork/lnd/kvdb"
|
|
)
|
|
|
|
var (
|
|
// paymentsRootBucket is the name of the top-level bucket within the
|
|
// database that stores all data related to payments.
|
|
paymentsRootBucket = []byte("payments-root-bucket")
|
|
|
|
// paymentHtlcsBucket is a bucket where we'll store the information
|
|
// about the HTLCs that were attempted for a payment.
|
|
paymentHtlcsBucket = []byte("payment-htlcs-bucket")
|
|
|
|
// oldAttemptInfoKey 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.
|
|
oldAttemptInfoKey = []byte("htlc-attempt-info")
|
|
|
|
// oldSettleInfoKey is a key used in a HTLC's sub-bucket to store the
|
|
// settle info, if any.
|
|
oldSettleInfoKey = []byte("htlc-settle-info")
|
|
|
|
// oldFailInfoKey is a key used in a HTLC's sub-bucket to store
|
|
// failure information, if any.
|
|
oldFailInfoKey = []byte("htlc-fail-info")
|
|
|
|
// htlcAttemptInfoKey is the key used as the prefix of an HTLC attempt
|
|
// to store the info about the attempt that was done for the HTLC in
|
|
// question. The HTLC attempt ID is concatenated at the end.
|
|
htlcAttemptInfoKey = []byte("ai")
|
|
|
|
// htlcSettleInfoKey is the key used as the prefix of an HTLC attempt
|
|
// settle info, if any. The HTLC attempt ID is concatenated at the end.
|
|
htlcSettleInfoKey = []byte("si")
|
|
|
|
// htlcFailInfoKey is the key used as the prefix of an HTLC attempt
|
|
// failure information, if any.The HTLC attempt ID is concatenated at
|
|
// the end.
|
|
htlcFailInfoKey = []byte("fi")
|
|
)
|
|
|
|
// htlcBucketKey creates a composite key from prefix and id where the result is
|
|
// simply the two concatenated. This is the exact copy from payments.go.
|
|
func htlcBucketKey(prefix, id []byte) []byte {
|
|
key := make([]byte, len(prefix)+len(id))
|
|
copy(key, prefix)
|
|
copy(key[len(prefix):], id)
|
|
return key
|
|
}
|
|
|
|
// MigrateHtlcAttempts will gather all htlc-attempt-info's, htlcs-settle-info's
|
|
// and htlcs-fail-info's from the attempt ID buckes and re-store them using the
|
|
// flattened keys to each payment's payment-htlcs-bucket.
|
|
func MigrateHtlcAttempts(tx kvdb.RwTx) error {
|
|
payments := tx.ReadWriteBucket(paymentsRootBucket)
|
|
if payments == nil {
|
|
return nil
|
|
}
|
|
|
|
// Collect all payment hashes so we can migrate payments one-by-one to
|
|
// avoid any bugs bbolt might have when invalidating cursors.
|
|
// For 100 million payments, this would need about 3 GiB memory so we
|
|
// should hopefully be fine for very large nodes too.
|
|
var paymentHashes []string
|
|
if err := payments.ForEach(func(hash, v []byte) error {
|
|
// Get the bucket which contains the payment, fail if the key
|
|
// does not have a bucket.
|
|
bucket := payments.NestedReadBucket(hash)
|
|
if bucket == nil {
|
|
return fmt.Errorf("key must be a bucket: '%v'",
|
|
string(paymentsRootBucket))
|
|
}
|
|
|
|
paymentHashes = append(paymentHashes, string(hash))
|
|
return nil
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, paymentHash := range paymentHashes {
|
|
payment := payments.NestedReadWriteBucket([]byte(paymentHash))
|
|
if payment.Get(paymentHtlcsBucket) != nil {
|
|
return fmt.Errorf("key must be a bucket: '%v'",
|
|
string(paymentHtlcsBucket))
|
|
}
|
|
|
|
htlcs := payment.NestedReadWriteBucket(paymentHtlcsBucket)
|
|
if htlcs == nil {
|
|
// Nothing to migrate for this payment.
|
|
continue
|
|
}
|
|
|
|
if err := migrateHtlcsBucket(htlcs); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// migrateHtlcsBucket is a helper to gather, transform and re-store htlc attempt
|
|
// key/values.
|
|
func migrateHtlcsBucket(htlcs kvdb.RwBucket) error {
|
|
// Collect attempt ids so that we can migrate attempts one-by-one
|
|
// to avoid any bugs bbolt might have when invalidating cursors.
|
|
var aids []string
|
|
|
|
// First we collect all htlc attempt ids.
|
|
if err := htlcs.ForEach(func(aid, v []byte) error {
|
|
aids = append(aids, string(aid))
|
|
return nil
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Next we go over these attempts, fetch all data and migrate.
|
|
for _, aid := range aids {
|
|
aidKey := []byte(aid)
|
|
attempt := htlcs.NestedReadWriteBucket(aidKey)
|
|
if attempt == nil {
|
|
return fmt.Errorf("non bucket element '%v' in '%v' "+
|
|
"bucket", aidKey, string(paymentHtlcsBucket))
|
|
}
|
|
|
|
// Collect attempt/settle/fail infos.
|
|
attemptInfo := attempt.Get(oldAttemptInfoKey)
|
|
if len(attemptInfo) > 0 {
|
|
newKey := htlcBucketKey(htlcAttemptInfoKey, aidKey)
|
|
if err := htlcs.Put(newKey, attemptInfo); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
settleInfo := attempt.Get(oldSettleInfoKey)
|
|
if len(settleInfo) > 0 {
|
|
newKey := htlcBucketKey(htlcSettleInfoKey, aidKey)
|
|
if err := htlcs.Put(newKey, settleInfo); err != nil {
|
|
return err
|
|
}
|
|
|
|
}
|
|
|
|
failInfo := attempt.Get(oldFailInfoKey)
|
|
if len(failInfo) > 0 {
|
|
newKey := htlcBucketKey(htlcFailInfoKey, aidKey)
|
|
if err := htlcs.Put(newKey, failInfo); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
// Finally we delete old attempt buckets.
|
|
for _, aid := range aids {
|
|
if err := htlcs.DeleteNestedBucket([]byte(aid)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|