github.com/decred/dcrlnd@v0.7.6/htlcswitch/sequencer.go (about) 1 package htlcswitch 2 3 import ( 4 "sync" 5 6 "github.com/decred/dcrlnd/channeldb" 7 "github.com/decred/dcrlnd/kvdb" 8 "github.com/go-errors/errors" 9 ) 10 11 // defaultSequenceBatchSize specifies the window of sequence numbers that are 12 // allocated for each write to disk made by the sequencer. 13 const defaultSequenceBatchSize = 1000 14 15 // Sequencer emits sequence numbers for locally initiated HTLCs. These are 16 // only used internally for tracking pending payments, however they must be 17 // unique in order to avoid circuit key collision in the circuit map. 18 type Sequencer interface { 19 // NextID returns a unique sequence number for each invocation. 20 NextID() (uint64, error) 21 } 22 23 var ( 24 // nextPaymentIDKey identifies the bucket that will keep track of the 25 // persistent sequence numbers for payments. 26 nextPaymentIDKey = []byte("next-payment-id-key") 27 28 // ErrSequencerCorrupted signals that the persistence engine was not 29 // initialized, or has been corrupted since startup. 30 ErrSequencerCorrupted = errors.New( 31 "sequencer database has been corrupted") 32 ) 33 34 // persistentSequencer is a concrete implementation of IDGenerator, that uses 35 // channeldb to allocate sequence numbers. 36 type persistentSequencer struct { 37 db *channeldb.DB 38 39 mu sync.Mutex 40 41 nextID uint64 42 horizonID uint64 43 } 44 45 // NewPersistentSequencer initializes a new sequencer using a channeldb backend. 46 func NewPersistentSequencer(db *channeldb.DB) (Sequencer, error) { 47 g := &persistentSequencer{ 48 db: db, 49 } 50 51 // Ensure the database bucket is created before any updates are 52 // performed. 53 if err := g.initDB(); err != nil { 54 return nil, err 55 } 56 57 return g, nil 58 } 59 60 // NextID returns a unique sequence number for every invocation, persisting the 61 // assignment to avoid reuse. 62 func (s *persistentSequencer) NextID() (uint64, error) { 63 64 // nextID will be the unique sequence number returned if no errors are 65 // encountered. 66 var nextID uint64 67 68 // If our sequence batch has not been exhausted, we can allocate the 69 // next identifier in the range. 70 s.mu.Lock() 71 defer s.mu.Unlock() 72 73 if s.nextID < s.horizonID { 74 nextID = s.nextID 75 s.nextID++ 76 77 return nextID, nil 78 } 79 80 // Otherwise, our sequence batch has been exhausted. We use the last 81 // known sequence number on disk to mark the beginning of the next 82 // sequence batch, and allocate defaultSequenceBatchSize (1000) at a 83 // time. 84 // 85 // NOTE: This also will happen on the first invocation after startup, 86 // i.e. when nextID and horizonID are both 0. The next sequence batch to be 87 // allocated will start from the last known tip on disk, which is fine 88 // as we only require uniqueness of the allocated numbers. 89 var nextHorizonID uint64 90 if err := kvdb.Update(s.db, func(tx kvdb.RwTx) error { 91 nextIDBkt := tx.ReadWriteBucket(nextPaymentIDKey) 92 if nextIDBkt == nil { 93 return ErrSequencerCorrupted 94 } 95 96 nextID = nextIDBkt.Sequence() 97 nextHorizonID = nextID + defaultSequenceBatchSize 98 99 // Cannot fail when used in Update. 100 nextIDBkt.SetSequence(nextHorizonID) 101 102 return nil 103 }, func() { 104 nextHorizonID = 0 105 }); err != nil { 106 return 0, err 107 } 108 109 // Never assign index zero, to avoid collisions with the EmptyKeystone. 110 if nextID == 0 { 111 nextID++ 112 } 113 114 // If our batch sequence allocation succeed, update our in-memory values 115 // so we can continue to allocate sequence numbers without hitting disk. 116 // The nextID is incremented by one in memory so the in can be used 117 // issued directly on the next invocation. 118 s.nextID = nextID + 1 119 s.horizonID = nextHorizonID 120 121 return nextID, nil 122 } 123 124 // initDB populates the bucket used to generate payment sequence numbers. 125 func (s *persistentSequencer) initDB() error { 126 return kvdb.Update(s.db, func(tx kvdb.RwTx) error { 127 _, err := tx.CreateTopLevelBucket(nextPaymentIDKey) 128 return err 129 }, func() {}) 130 }