@ -6,11 +6,18 @@ import (
"errors"
"fmt"
"io"
"sync"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lntypes"
)
const (
// paymentSeqBlockSize is the block size used when we batch allocate
// payment sequences for future payments.
paymentSeqBlockSize = 1000
)
var (
// ErrAlreadyPaid signals we have already paid this payment hash.
ErrAlreadyPaid = errors . New ( "invoice is already paid" )
@ -84,7 +91,10 @@ var (
// PaymentControl implements persistence for payments and payment attempts.
type PaymentControl struct {
db * DB
paymentSeqMx sync . Mutex
currPaymentSeq uint64
storedPaymentSeq uint64
db * DB
}
// NewPaymentControl creates a new instance of the PaymentControl.
@ -101,6 +111,14 @@ func NewPaymentControl(db *DB) *PaymentControl {
func ( p * PaymentControl ) InitPayment ( paymentHash lntypes . Hash ,
info * PaymentCreationInfo ) error {
// Obtain a new sequence number for this payment. This is used
// to sort the payments in order of creation, and also acts as
// a unique identifier for each payment.
sequenceNum , err := p . nextPaymentSequence ( )
if err != nil {
return err
}
var b bytes . Buffer
if err := serializePaymentCreationInfo ( & b , info ) ; err != nil {
return err
@ -108,7 +126,7 @@ func (p *PaymentControl) InitPayment(paymentHash lntypes.Hash,
infoBytes := b . Bytes ( )
var updateErr error
err : = kvdb . Batch ( p . db . Backend , func ( tx kvdb . RwTx ) error {
err = kvdb . Batch ( p . db . Backend , func ( tx kvdb . RwTx ) error {
// Reset the update error, to avoid carrying over an error
// from a previous execution of the batched db transaction.
updateErr = nil
@ -150,14 +168,6 @@ func (p *PaymentControl) InitPayment(paymentHash lntypes.Hash,
return nil
}
// Obtain a new sequence number for this payment. This is used
// to sort the payments in order of creation, and also acts as
// a unique identifier for each payment.
sequenceNum , err := nextPaymentSequence ( tx )
if err != nil {
return err
}
// Before we set our new sequence number, we check whether this
// payment has a previously set sequence number and remove its
// index entry if it exists. This happens in the case where we
@ -615,19 +625,45 @@ func fetchPaymentBucketUpdate(tx kvdb.RwTx, paymentHash lntypes.Hash) (
// nextPaymentSequence returns the next sequence number to store for a new
// payment.
func nextPaymentSequence ( tx kvdb . RwTx ) ( [ ] byte , error ) {
payments , err := tx . CreateTopLevelBucket ( paymentsRootBucket )
if err != nil {
return nil , err
}
func ( p * PaymentControl ) nextPaymentSequence ( ) ( [ ] byte , error ) {
p . paymentSeqMx . Lock ( )
defer p . paymentSeqMx . Unlock ( )
// Set a new upper bound in the DB every 1000 payments to avoid
// conflicts on the sequence when using etcd.
if p . currPaymentSeq == p . storedPaymentSeq {
var currPaymentSeq , newUpperBound uint64
if err := kvdb . Update ( p . db . Backend , func ( tx kvdb . RwTx ) error {
paymentsBucket , err := tx . CreateTopLevelBucket (
paymentsRootBucket ,
)
if err != nil {
return err
}
seq , err := payments . NextSequence ( )
if err != nil {
return nil , err
currPaymentSeq = paymentsBucket . Sequence ( )
newUpperBound = currPaymentSeq + paymentSeqBlockSize
return paymentsBucket . SetSequence ( newUpperBound )
} , func ( ) { } ) ; err != nil {
return nil , err
}
// We lazy initialize the cached currPaymentSeq here using the
// first nextPaymentSequence() call. This if statement will auto
// initialize our stored currPaymentSeq, since by default both
// this variable and storedPaymentSeq are zero which in turn
// will have us fetch the current values from the DB.
if p . currPaymentSeq == 0 {
p . currPaymentSeq = currPaymentSeq
}
p . storedPaymentSeq = newUpperBound
}
p . currPaymentSeq ++
b := make ( [ ] byte , 8 )
binary . BigEndian . PutUint64 ( b , seq )
binary . BigEndian . PutUint64 ( b , p . currPaymentSeq )
return b , nil
}