github.com/decred/dcrlnd@v0.7.6/channeldb/migration13/migration.go (about) 1 package migration13 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 7 "github.com/decred/dcrlnd/kvdb" 8 ) 9 10 var ( 11 paymentsRootBucket = []byte("payments-root-bucket") 12 13 // paymentCreationInfoKey is a key used in the payment's sub-bucket to 14 // store the creation info of the payment. 15 paymentCreationInfoKey = []byte("payment-creation-info") 16 17 // paymentFailInfoKey is a key used in the payment's sub-bucket to 18 // store information about the reason a payment failed. 19 paymentFailInfoKey = []byte("payment-fail-info") 20 21 // paymentAttemptInfoKey is a key used in the payment's sub-bucket to 22 // store the info about the latest attempt that was done for the 23 // payment in question. 24 paymentAttemptInfoKey = []byte("payment-attempt-info") 25 26 // paymentSettleInfoKey is a key used in the payment's sub-bucket to 27 // store the settle info of the payment. 28 paymentSettleInfoKey = []byte("payment-settle-info") 29 30 // paymentHtlcsBucket is a bucket where we'll store the information 31 // about the HTLCs that were attempted for a payment. 32 paymentHtlcsBucket = []byte("payment-htlcs-bucket") 33 34 // htlcAttemptInfoKey is a key used in a HTLC's sub-bucket to store the 35 // info about the attempt that was done for the HTLC in question. 36 htlcAttemptInfoKey = []byte("htlc-attempt-info") 37 38 // htlcSettleInfoKey is a key used in a HTLC's sub-bucket to store the 39 // settle info, if any. 40 htlcSettleInfoKey = []byte("htlc-settle-info") 41 42 // htlcFailInfoKey is a key used in a HTLC's sub-bucket to store 43 // failure information, if any. 44 htlcFailInfoKey = []byte("htlc-fail-info") 45 46 byteOrder = binary.BigEndian 47 ) 48 49 // MigrateMPP migrates the payments to a new structure that accommodates for mpp 50 // payments. 51 func MigrateMPP(tx kvdb.RwTx) error { 52 log.Infof("Migrating payments to mpp structure") 53 54 // Iterate over all payments and store their indexing keys. This is 55 // needed, because no modifications are allowed inside a Bucket.ForEach 56 // loop. 57 paymentsBucket := tx.ReadWriteBucket(paymentsRootBucket) 58 if paymentsBucket == nil { 59 return nil 60 } 61 62 var paymentKeys [][]byte 63 err := paymentsBucket.ForEach(func(k, v []byte) error { 64 paymentKeys = append(paymentKeys, k) 65 return nil 66 }) 67 if err != nil { 68 return err 69 } 70 71 // With all keys retrieved, start the migration. 72 for _, k := range paymentKeys { 73 bucket := paymentsBucket.NestedReadWriteBucket(k) 74 75 // We only expect sub-buckets to be found in 76 // this top-level bucket. 77 if bucket == nil { 78 return fmt.Errorf("non bucket element in " + 79 "payments bucket") 80 } 81 82 // Fetch old format creation info. 83 creationInfo := bucket.Get(paymentCreationInfoKey) 84 if creationInfo == nil { 85 return fmt.Errorf("creation info not found") 86 } 87 88 // Make a copy because bbolt doesn't allow this value to be 89 // changed in-place. 90 newCreationInfo := make([]byte, len(creationInfo)) 91 copy(newCreationInfo, creationInfo) 92 93 // Convert to nano seconds. 94 timeBytes := newCreationInfo[32+8 : 32+8+8] 95 time := byteOrder.Uint64(timeBytes) 96 timeNs := time * 1000000000 97 byteOrder.PutUint64(timeBytes, timeNs) 98 99 // Write back new format creation info. 100 err := bucket.Put(paymentCreationInfoKey, newCreationInfo) 101 if err != nil { 102 return err 103 } 104 105 // No migration needed if there is no attempt stored. 106 attemptInfo := bucket.Get(paymentAttemptInfoKey) 107 if attemptInfo == nil { 108 continue 109 } 110 111 // Delete attempt info on the payment level. 112 if err := bucket.Delete(paymentAttemptInfoKey); err != nil { 113 return err 114 } 115 116 // Save attempt id for later use. 117 attemptID := attemptInfo[:8] 118 119 // Discard attempt id. It will become a bucket key in the new 120 // structure. 121 attemptInfo = attemptInfo[8:] 122 123 // Append unknown (zero) attempt time. 124 var zero [8]byte 125 attemptInfo = append(attemptInfo, zero[:]...) 126 127 // Create bucket that contains all htlcs. 128 htlcsBucket, err := bucket.CreateBucket(paymentHtlcsBucket) 129 if err != nil { 130 return err 131 } 132 133 // Create an htlc for this attempt. 134 htlcBucket, err := htlcsBucket.CreateBucket(attemptID) 135 if err != nil { 136 return err 137 } 138 139 // Save migrated attempt info. 140 err = htlcBucket.Put(htlcAttemptInfoKey, attemptInfo) 141 if err != nil { 142 return err 143 } 144 145 // Migrate settle info. 146 settleInfo := bucket.Get(paymentSettleInfoKey) 147 if settleInfo != nil { 148 // Payment-level settle info can be deleted. 149 err := bucket.Delete(paymentSettleInfoKey) 150 if err != nil { 151 return err 152 } 153 154 // Append unknown (zero) settle time. 155 settleInfo = append(settleInfo, zero[:]...) 156 157 // Save settle info. 158 err = htlcBucket.Put(htlcSettleInfoKey, settleInfo) 159 if err != nil { 160 return err 161 } 162 163 // Migration for settled htlc completed. 164 continue 165 } 166 167 // If there is no payment-level failure reason, the payment is 168 // still in flight and nothing else needs to be migrated. 169 // Otherwise the payment-level failure reason can remain 170 // unchanged. 171 inFlight := bucket.Get(paymentFailInfoKey) == nil 172 if inFlight { 173 continue 174 } 175 176 // The htlc failed. Add htlc fail info with reason unknown. We 177 // don't have access to the original failure reason anymore. 178 failInfo := []byte{ 179 // Fail time unknown. 180 0, 0, 0, 0, 0, 0, 0, 0, 181 182 // Zero length wire message. 183 0, 184 185 // Failure reason unknown. 186 0, 187 188 // Failure source index zero. 189 0, 0, 0, 0, 190 } 191 192 // Save fail info. 193 err = htlcBucket.Put(htlcFailInfoKey, failInfo) 194 if err != nil { 195 return err 196 } 197 } 198 199 log.Infof("Migration of payments to mpp structure complete!") 200 201 return nil 202 }