github.com/decred/dcrlnd@v0.7.6/channeldb/duplicate_payments.go (about) 1 package channeldb 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "io" 8 "time" 9 10 "github.com/decred/dcrd/dcrec/secp256k1/v4" 11 "github.com/decred/dcrlnd/kvdb" 12 "github.com/decred/dcrlnd/lntypes" 13 "github.com/decred/dcrlnd/lnwire" 14 "github.com/decred/dcrlnd/routing/route" 15 ) 16 17 var ( 18 // duplicatePaymentsBucket is the name of a optional sub-bucket within 19 // the payment hash bucket, that is used to hold duplicate payments to a 20 // payment hash. This is needed to support information from earlier 21 // versions of lnd, where it was possible to pay to a payment hash more 22 // than once. 23 duplicatePaymentsBucket = []byte("payment-duplicate-bucket") 24 25 // duplicatePaymentSettleInfoKey is a key used in the payment's 26 // sub-bucket to store the settle info of the payment. 27 duplicatePaymentSettleInfoKey = []byte("payment-settle-info") 28 29 // duplicatePaymentAttemptInfoKey is a key used in the payment's 30 // sub-bucket to store the info about the latest attempt that was done 31 // for the payment in question. 32 duplicatePaymentAttemptInfoKey = []byte("payment-attempt-info") 33 34 // duplicatePaymentCreationInfoKey is a key used in the payment's 35 // sub-bucket to store the creation info of the payment. 36 duplicatePaymentCreationInfoKey = []byte("payment-creation-info") 37 38 // duplicatePaymentFailInfoKey is a key used in the payment's sub-bucket 39 // to store information about the reason a payment failed. 40 duplicatePaymentFailInfoKey = []byte("payment-fail-info") 41 42 // duplicatePaymentSequenceKey is a key used in the payment's sub-bucket 43 // to store the sequence number of the payment. 44 duplicatePaymentSequenceKey = []byte("payment-sequence-key") 45 ) 46 47 // duplicateHTLCAttemptInfo contains static information about a specific HTLC 48 // attempt for a payment. This information is used by the router to handle any 49 // errors coming back after an attempt is made, and to query the switch about 50 // the status of the attempt. 51 type duplicateHTLCAttemptInfo struct { 52 // attemptID is the unique ID used for this attempt. 53 attemptID uint64 54 55 // sessionKey is the ephemeral key used for this attempt. 56 sessionKey [secp256k1.PrivKeyBytesLen]byte 57 58 // route is the route attempted to send the HTLC. 59 route route.Route 60 } 61 62 // fetchDuplicatePaymentStatus fetches the payment status of the payment. If the 63 // payment isn't found, it will default to "StatusUnknown". 64 func fetchDuplicatePaymentStatus(bucket kvdb.RBucket) PaymentStatus { 65 if bucket.Get(duplicatePaymentSettleInfoKey) != nil { 66 return StatusSucceeded 67 } 68 69 if bucket.Get(duplicatePaymentFailInfoKey) != nil { 70 return StatusFailed 71 } 72 73 if bucket.Get(duplicatePaymentCreationInfoKey) != nil { 74 return StatusInFlight 75 } 76 77 return StatusUnknown 78 } 79 80 func deserializeDuplicateHTLCAttemptInfo(r io.Reader) ( 81 *duplicateHTLCAttemptInfo, error) { 82 83 a := &duplicateHTLCAttemptInfo{} 84 err := ReadElements(r, &a.attemptID, &a.sessionKey) 85 if err != nil { 86 return nil, err 87 } 88 a.route, err = DeserializeRoute(r) 89 if err != nil { 90 return nil, err 91 } 92 return a, nil 93 } 94 95 func deserializeDuplicatePaymentCreationInfo(r io.Reader) ( 96 *PaymentCreationInfo, error) { 97 98 var scratch [8]byte 99 100 c := &PaymentCreationInfo{} 101 102 if _, err := io.ReadFull(r, c.PaymentIdentifier[:]); err != nil { 103 return nil, err 104 } 105 106 if _, err := io.ReadFull(r, scratch[:]); err != nil { 107 return nil, err 108 } 109 c.Value = lnwire.MilliAtom(byteOrder.Uint64(scratch[:])) 110 111 if _, err := io.ReadFull(r, scratch[:]); err != nil { 112 return nil, err 113 } 114 c.CreationTime = time.Unix(int64(byteOrder.Uint64(scratch[:])), 0) 115 116 if _, err := io.ReadFull(r, scratch[:4]); err != nil { 117 return nil, err 118 } 119 120 reqLen := byteOrder.Uint32(scratch[:4]) 121 payReq := make([]byte, reqLen) 122 if reqLen > 0 { 123 if _, err := io.ReadFull(r, payReq); err != nil { 124 return nil, err 125 } 126 } 127 c.PaymentRequest = payReq 128 129 return c, nil 130 } 131 132 func fetchDuplicatePayment(bucket kvdb.RBucket) (*MPPayment, error) { 133 seqBytes := bucket.Get(duplicatePaymentSequenceKey) 134 if seqBytes == nil { 135 return nil, fmt.Errorf("sequence number not found") 136 } 137 138 sequenceNum := binary.BigEndian.Uint64(seqBytes) 139 140 // Get the payment status. 141 paymentStatus := fetchDuplicatePaymentStatus(bucket) 142 143 // Get the PaymentCreationInfo. 144 b := bucket.Get(duplicatePaymentCreationInfoKey) 145 if b == nil { 146 return nil, fmt.Errorf("creation info not found") 147 } 148 149 r := bytes.NewReader(b) 150 creationInfo, err := deserializeDuplicatePaymentCreationInfo(r) 151 if err != nil { 152 return nil, err 153 154 } 155 156 // Get failure reason if available. 157 var failureReason *FailureReason 158 b = bucket.Get(duplicatePaymentFailInfoKey) 159 if b != nil { 160 reason := FailureReason(b[0]) 161 failureReason = &reason 162 } 163 164 payment := &MPPayment{ 165 SequenceNum: sequenceNum, 166 Info: creationInfo, 167 FailureReason: failureReason, 168 Status: paymentStatus, 169 } 170 171 // Get the HTLCAttemptInfo. It can be absent. 172 b = bucket.Get(duplicatePaymentAttemptInfoKey) 173 if b != nil { 174 r = bytes.NewReader(b) 175 attempt, err := deserializeDuplicateHTLCAttemptInfo(r) 176 if err != nil { 177 return nil, err 178 } 179 180 htlc := HTLCAttempt{ 181 HTLCAttemptInfo: HTLCAttemptInfo{ 182 AttemptID: attempt.attemptID, 183 Route: attempt.route, 184 sessionKey: attempt.sessionKey, 185 }, 186 } 187 188 // Get the payment preimage. This is only found for 189 // successful payments. 190 b = bucket.Get(duplicatePaymentSettleInfoKey) 191 if b != nil { 192 var preimg lntypes.Preimage 193 copy(preimg[:], b) 194 195 htlc.Settle = &HTLCSettleInfo{ 196 Preimage: preimg, 197 SettleTime: time.Time{}, 198 } 199 } else { 200 // Otherwise the payment must have failed. 201 htlc.Failure = &HTLCFailInfo{ 202 FailTime: time.Time{}, 203 } 204 } 205 206 payment.HTLCs = []HTLCAttempt{htlc} 207 } 208 209 return payment, nil 210 } 211 212 func fetchDuplicatePayments(paymentHashBucket kvdb.RBucket) ([]*MPPayment, 213 error) { 214 215 var payments []*MPPayment 216 217 // For older versions of lnd, duplicate payments to a payment has was 218 // possible. These will be found in a sub-bucket indexed by their 219 // sequence number if available. 220 dup := paymentHashBucket.NestedReadBucket(duplicatePaymentsBucket) 221 if dup == nil { 222 return nil, nil 223 } 224 225 err := dup.ForEach(func(k, v []byte) error { 226 subBucket := dup.NestedReadBucket(k) 227 if subBucket == nil { 228 // We one bucket for each duplicate to be found. 229 return fmt.Errorf("non bucket element" + 230 "in duplicate bucket") 231 } 232 233 p, err := fetchDuplicatePayment(subBucket) 234 if err != nil { 235 return err 236 } 237 238 payments = append(payments, p) 239 return nil 240 }) 241 if err != nil { 242 return nil, err 243 } 244 245 return payments, nil 246 }