800eea931f
In this commit, we switch from boltbd/bolt to coreos/bbolt as the former is no longer being actively maintained.
129 lines
3.6 KiB
Go
129 lines
3.6 KiB
Go
package htlcswitch
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/coreos/bbolt"
|
|
"github.com/go-errors/errors"
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
)
|
|
|
|
// defaultSequenceBatchSize specifies the window of sequence numbers that are
|
|
// allocated for each write to disk made by the sequencer.
|
|
const defaultSequenceBatchSize = 1000
|
|
|
|
// Sequencer emits sequence numbers for locally initiated HTLCs. These are
|
|
// only used internally for tracking pending payments, however they must be
|
|
// unique in order to avoid circuit key collision in the circuit map.
|
|
type Sequencer interface {
|
|
// NextID returns a unique sequence number for each invocation.
|
|
NextID() (uint64, error)
|
|
}
|
|
|
|
var (
|
|
// nextPaymentIDKey identifies the bucket that will keep track of the
|
|
// persistent sequence numbers for payments.
|
|
nextPaymentIDKey = []byte("next-payment-id-key")
|
|
|
|
// ErrSequencerCorrupted signals that the persistence engine was not
|
|
// initialized, or has been corrupted since startup.
|
|
ErrSequencerCorrupted = errors.New(
|
|
"sequencer database has been corrupted")
|
|
)
|
|
|
|
// persistentSequencer is a concrete implementation of IDGenerator, that uses
|
|
// channeldb to allocate sequence numbers.
|
|
type persistentSequencer struct {
|
|
db *channeldb.DB
|
|
|
|
mu sync.Mutex
|
|
|
|
nextID uint64
|
|
horizonID uint64
|
|
}
|
|
|
|
// NewPersistentSequencer initializes a new sequencer using a channeldb backend.
|
|
func NewPersistentSequencer(db *channeldb.DB) (Sequencer, error) {
|
|
g := &persistentSequencer{
|
|
db: db,
|
|
}
|
|
|
|
// Ensure the database bucket is created before any updates are
|
|
// performed.
|
|
if err := g.initDB(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return g, nil
|
|
}
|
|
|
|
// NextID returns a unique sequence number for every invocation, persisting the
|
|
// assignment to avoid reuse.
|
|
func (s *persistentSequencer) NextID() (uint64, error) {
|
|
|
|
// nextID will be the unique sequence number returned if no errors are
|
|
// encountered.
|
|
var nextID uint64
|
|
|
|
// If our sequence batch has not been exhausted, we can allocate the
|
|
// next identifier in the range.
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if s.nextID < s.horizonID {
|
|
nextID = s.nextID
|
|
s.nextID++
|
|
|
|
return nextID, nil
|
|
}
|
|
|
|
// Otherwise, our sequence batch has been exhausted. We use the last
|
|
// known sequence number on disk to mark the beginning of the next
|
|
// sequence batch, and allocate defaultSequenceBatchSize (1000) at a
|
|
// time.
|
|
//
|
|
// NOTE: This also will happen on the first invocation after startup,
|
|
// i.e. when nextID and horizonID are both 0. The next sequence batch to be
|
|
// allocated will start from the last known tip on disk, which is fine
|
|
// as we only require uniqueness of the allocated numbers.
|
|
var nextHorizonID uint64
|
|
if err := s.db.Update(func(tx *bolt.Tx) error {
|
|
nextIDBkt := tx.Bucket(nextPaymentIDKey)
|
|
if nextIDBkt == nil {
|
|
return ErrSequencerCorrupted
|
|
}
|
|
|
|
nextID = nextIDBkt.Sequence()
|
|
nextHorizonID = nextID + defaultSequenceBatchSize
|
|
|
|
// Cannot fail when used in Update.
|
|
nextIDBkt.SetSequence(nextHorizonID)
|
|
|
|
return nil
|
|
}); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// Never assign index zero, to avoid collisions with the EmptyKeystone.
|
|
if nextID == 0 {
|
|
nextID++
|
|
}
|
|
|
|
// If our batch sequence allocation succeed, update our in-memory values
|
|
// so we can continue to allocate sequence numbers without hitting disk.
|
|
// The nextID is incremented by one in memory so the in can be used
|
|
// issued directly on the next invocation.
|
|
s.nextID = nextID + 1
|
|
s.horizonID = nextHorizonID
|
|
|
|
return nextID, nil
|
|
}
|
|
|
|
// initDB populates the bucket used to generate payment sequence numbers.
|
|
func (s *persistentSequencer) initDB() error {
|
|
return s.db.Update(func(tx *bolt.Tx) error {
|
|
_, err := tx.CreateBucketIfNotExists(nextPaymentIDKey)
|
|
return err
|
|
})
|
|
}
|