github.com/decred/dcrlnd@v0.7.6/channeldb/migration16/migration.go (about) 1 package migration16 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 9 "github.com/decred/dcrd/wire" 10 "github.com/decred/dcrlnd/kvdb" 11 ) 12 13 var ( 14 paymentsRootBucket = []byte("payments-root-bucket") 15 16 paymentSequenceKey = []byte("payment-sequence-key") 17 18 duplicatePaymentsBucket = []byte("payment-duplicate-bucket") 19 20 paymentsIndexBucket = []byte("payments-index-bucket") 21 22 byteOrder = binary.BigEndian 23 ) 24 25 // paymentIndexType indicates the type of index we have recorded in the payment 26 // indexes bucket. 27 type paymentIndexType uint8 28 29 // paymentIndexTypeHash is a payment index type which indicates that we have 30 // created an index of payment sequence number to payment hash. 31 const paymentIndexTypeHash paymentIndexType = 0 32 33 // paymentIndex stores all the information we require to create an index by 34 // sequence number for a payment. 35 type paymentIndex struct { 36 // paymentHash is the hash of the payment, which is its key in the 37 // payment root bucket. 38 paymentHash []byte 39 40 // sequenceNumbers is the set of sequence numbers associated with this 41 // payment hash. There will be more than one sequence number in the 42 // case where duplicate payments are present. 43 sequenceNumbers [][]byte 44 } 45 46 // MigrateSequenceIndex migrates the payments db to contain a new bucket which 47 // provides an index from sequence number to payment hash. This is required 48 // for more efficient sequential lookup of payments, which are keyed by payment 49 // hash before this migration. 50 func MigrateSequenceIndex(tx kvdb.RwTx) error { 51 log.Infof("Migrating payments to add sequence number index") 52 53 // Get a list of indices we need to write. 54 indexList, err := getPaymentIndexList(tx) 55 if err != nil { 56 return err 57 } 58 59 // Create the top level bucket that we will use to index payments in. 60 bucket, err := tx.CreateTopLevelBucket(paymentsIndexBucket) 61 if err != nil { 62 return err 63 } 64 65 // Write an index for each of our payments. 66 for _, index := range indexList { 67 // Write indexes for each of our sequence numbers. 68 for _, seqNr := range index.sequenceNumbers { 69 err := putIndex(bucket, seqNr, index.paymentHash) 70 if err != nil { 71 return err 72 } 73 } 74 } 75 76 return nil 77 } 78 79 // putIndex performs a sanity check that ensures we are not writing duplicate 80 // indexes to disk then creates the index provided. 81 func putIndex(bucket kvdb.RwBucket, sequenceNr, paymentHash []byte) error { 82 // Add a sanity check that we do not already have an entry with 83 // this sequence number. 84 existingEntry := bucket.Get(sequenceNr) 85 if existingEntry != nil { 86 return fmt.Errorf("sequence number: %x duplicated", 87 sequenceNr) 88 } 89 90 bytes, err := serializePaymentIndexEntry(paymentHash) 91 if err != nil { 92 return err 93 } 94 95 return bucket.Put(sequenceNr, bytes) 96 } 97 98 // serializePaymentIndexEntry serializes a payment hash typed index. The value 99 // produced contains a payment index type (which can be used in future to 100 // signal different payment index types) and the payment hash. 101 func serializePaymentIndexEntry(hash []byte) ([]byte, error) { 102 var b bytes.Buffer 103 104 err := binary.Write(&b, byteOrder, paymentIndexTypeHash) 105 if err != nil { 106 return nil, err 107 } 108 109 if err := wire.WriteVarBytes(&b, 0, hash); err != nil { 110 return nil, err 111 } 112 113 return b.Bytes(), nil 114 } 115 116 // getPaymentIndexList gets a list of indices we need to write for our current 117 // set of payments. 118 func getPaymentIndexList(tx kvdb.RTx) ([]paymentIndex, error) { 119 // Iterate over all payments and store their indexing keys. This is 120 // needed, because no modifications are allowed inside a Bucket.ForEach 121 // loop. 122 paymentsBucket := tx.ReadBucket(paymentsRootBucket) 123 if paymentsBucket == nil { 124 return nil, nil 125 } 126 127 var indexList []paymentIndex 128 err := paymentsBucket.ForEach(func(k, v []byte) error { 129 // Get the bucket which contains the payment, fail if the key 130 // does not have a bucket. 131 bucket := paymentsBucket.NestedReadBucket(k) 132 if bucket == nil { 133 return fmt.Errorf("non bucket element in " + 134 "payments bucket") 135 } 136 seqBytes := bucket.Get(paymentSequenceKey) 137 if seqBytes == nil { 138 return fmt.Errorf("nil sequence number bytes") 139 } 140 141 seqNrs, err := fetchSequenceNumbers(bucket) 142 if err != nil { 143 return err 144 } 145 146 // Create an index object with our payment hash and sequence 147 // numbers and append it to our set of indexes. 148 index := paymentIndex{ 149 paymentHash: k, 150 sequenceNumbers: seqNrs, 151 } 152 153 indexList = append(indexList, index) 154 return nil 155 }) 156 if err != nil { 157 return nil, err 158 } 159 160 return indexList, nil 161 } 162 163 // fetchSequenceNumbers fetches all the sequence numbers associated with a 164 // payment, including those belonging to any duplicate payments. 165 func fetchSequenceNumbers(paymentBucket kvdb.RBucket) ([][]byte, error) { 166 seqNum := paymentBucket.Get(paymentSequenceKey) 167 if seqNum == nil { 168 return nil, errors.New("expected sequence number") 169 } 170 171 sequenceNumbers := [][]byte{seqNum} 172 173 // Get the duplicate payments bucket, if it has no duplicates, just 174 // return early with the payment sequence number. 175 duplicates := paymentBucket.NestedReadBucket(duplicatePaymentsBucket) 176 if duplicates == nil { 177 return sequenceNumbers, nil 178 } 179 180 // If we do have duplicated, they are keyed by sequence number, so we 181 // iterate through the duplicates bucket and add them to our set of 182 // sequence numbers. 183 if err := duplicates.ForEach(func(k, v []byte) error { 184 sequenceNumbers = append(sequenceNumbers, k) 185 return nil 186 }); err != nil { 187 return nil, err 188 } 189 190 return sequenceNumbers, nil 191 }