@@ -2,8 +2,11 @@ package session
22
33import (
44 "bytes"
5+ "encoding/binary"
56 "errors"
67 "fmt"
8+ "os"
9+ "path/filepath"
710 "time"
811
912 "github.com/btcsuite/btcd/btcec/v2"
@@ -46,15 +49,125 @@ var (
4649 // IDs associated with the given group ID.
4750 sessionIDKey = []byte ("session-id" )
4851
49- // ErrSessionNotFound is an error returned when we attempt to retrieve
50- // information about a session but it is not found.
51- ErrSessionNotFound = errors .New ("session not found" )
52-
5352 // ErrDBInitErr is returned when a bucket that we expect to have been
5453 // set up during DB initialisation is not found.
5554 ErrDBInitErr = errors .New ("db did not initialise properly" )
55+
56+ // byteOrder is the default byte order we'll use for serialization
57+ // within the database.
58+ byteOrder = binary .BigEndian
59+ )
60+
61+ const (
62+ // DBFilename is the default filename of the session database.
63+ DBFilename = "session.db"
64+
65+ // dbFilePermission is the default permission the session database file
66+ // is created with.
67+ dbFilePermission = 0600
68+
69+ // DefaultSessionDBTimeout is the default maximum time we wait for the
70+ // session bbolt database to be opened. If the database is already
71+ // opened by another process, the unique lock cannot be obtained. With
72+ // the timeout we error out after the given time instead of just
73+ // blocking for forever.
74+ DefaultSessionDBTimeout = 5 * time .Second
5675)
5776
77+ // BoltStore is a bolt-backed persistent store.
78+ type BoltStore struct {
79+ * bbolt.DB
80+ }
81+
82+ // A compile-time check to ensure that BoltStore implements the Store interface.
83+ var _ Store = (* BoltStore )(nil )
84+
85+ // NewDB creates a new bolt database that can be found at the given directory.
86+ func NewDB (dir , fileName string ) (* BoltStore , error ) {
87+ firstInit := false
88+ path := filepath .Join (dir , fileName )
89+
90+ // If the database file does not exist yet, create its directory.
91+ if ! fileExists (path ) {
92+ if err := os .MkdirAll (dir , 0700 ); err != nil {
93+ return nil , err
94+ }
95+ firstInit = true
96+ }
97+
98+ db , err := initDB (path , firstInit )
99+ if err != nil {
100+ return nil , err
101+ }
102+
103+ // Attempt to sync the database's current version with the latest known
104+ // version available.
105+ if err := syncVersions (db ); err != nil {
106+ return nil , err
107+ }
108+
109+ return & BoltStore {DB : db }, nil
110+ }
111+
112+ // fileExists reports whether the named file or directory exists.
113+ func fileExists (path string ) bool {
114+ if _ , err := os .Stat (path ); err != nil {
115+ if os .IsNotExist (err ) {
116+ return false
117+ }
118+ }
119+ return true
120+ }
121+
122+ // initDB initializes all the required top-level buckets for the database.
123+ func initDB (filepath string , firstInit bool ) (* bbolt.DB , error ) {
124+ db , err := bbolt .Open (filepath , dbFilePermission , & bbolt.Options {
125+ Timeout : DefaultSessionDBTimeout ,
126+ })
127+ if err == bbolt .ErrTimeout {
128+ return nil , fmt .Errorf ("error while trying to open %s: timed " +
129+ "out after %v when trying to obtain exclusive lock" ,
130+ filepath , DefaultSessionDBTimeout )
131+ }
132+ if err != nil {
133+ return nil , err
134+ }
135+
136+ err = db .Update (func (tx * bbolt.Tx ) error {
137+ if firstInit {
138+ metadataBucket , err := tx .CreateBucketIfNotExists (
139+ metadataBucketKey ,
140+ )
141+ if err != nil {
142+ return err
143+ }
144+ err = setDBVersion (metadataBucket , latestDBVersion )
145+ if err != nil {
146+ return err
147+ }
148+ }
149+
150+ sessionBkt , err := tx .CreateBucketIfNotExists (sessionBucketKey )
151+ if err != nil {
152+ return err
153+ }
154+
155+ _ , err = sessionBkt .CreateBucketIfNotExists (idIndexKey )
156+ if err != nil {
157+ return err
158+ }
159+
160+ _ , err = sessionBkt .CreateBucketIfNotExists (groupIDIndexKey )
161+
162+ return err
163+ })
164+ if err != nil {
165+ return nil , err
166+ }
167+
168+ return db , nil
169+ }
170+
58171// getSessionKey returns the key for a session.
59172func getSessionKey (session * Session ) []byte {
60173 return session .LocalPublicKey .SerializeCompressed ()
@@ -64,7 +177,7 @@ func getSessionKey(session *Session) []byte {
64177// local public key already exists an error is returned.
65178//
66179// NOTE: this is part of the Store interface.
67- func (db * DB ) CreateSession (session * Session ) error {
180+ func (db * BoltStore ) CreateSession (session * Session ) error {
68181 var buf bytes.Buffer
69182 if err := SerializeSession (& buf , session ); err != nil {
70183 return err
@@ -158,7 +271,7 @@ func (db *DB) CreateSession(session *Session) error {
158271// to the session with the given local pub key.
159272//
160273// NOTE: this is part of the Store interface.
161- func (db * DB ) UpdateSessionRemotePubKey (localPubKey ,
274+ func (db * BoltStore ) UpdateSessionRemotePubKey (localPubKey ,
162275 remotePubKey * btcec.PublicKey ) error {
163276
164277 key := localPubKey .SerializeCompressed ()
@@ -196,7 +309,7 @@ func (db *DB) UpdateSessionRemotePubKey(localPubKey,
196309// GetSession fetches the session with the given key.
197310//
198311// NOTE: this is part of the Store interface.
199- func (db * DB ) GetSession (key * btcec.PublicKey ) (* Session , error ) {
312+ func (db * BoltStore ) GetSession (key * btcec.PublicKey ) (* Session , error ) {
200313 var session * Session
201314 err := db .View (func (tx * bbolt.Tx ) error {
202315 sessionBucket , err := getBucket (tx , sessionBucketKey )
@@ -226,7 +339,7 @@ func (db *DB) GetSession(key *btcec.PublicKey) (*Session, error) {
226339// ListSessions returns all sessions currently known to the store.
227340//
228341// NOTE: this is part of the Store interface.
229- func (db * DB ) ListSessions (filterFn func (s * Session ) bool ) ([]* Session , error ) {
342+ func (db * BoltStore ) ListSessions (filterFn func (s * Session ) bool ) ([]* Session , error ) {
230343 var sessions []* Session
231344 err := db .View (func (tx * bbolt.Tx ) error {
232345 sessionBucket , err := getBucket (tx , sessionBucketKey )
@@ -266,7 +379,7 @@ func (db *DB) ListSessions(filterFn func(s *Session) bool) ([]*Session, error) {
266379// public key to be revoked.
267380//
268381// NOTE: this is part of the Store interface.
269- func (db * DB ) RevokeSession (key * btcec.PublicKey ) error {
382+ func (db * BoltStore ) RevokeSession (key * btcec.PublicKey ) error {
270383 var session * Session
271384 return db .Update (func (tx * bbolt.Tx ) error {
272385 sessionBucket , err := getBucket (tx , sessionBucketKey )
@@ -299,7 +412,7 @@ func (db *DB) RevokeSession(key *btcec.PublicKey) error {
299412// GetSessionByID fetches the session with the given ID.
300413//
301414// NOTE: this is part of the Store interface.
302- func (db * DB ) GetSessionByID (id ID ) (* Session , error ) {
415+ func (db * BoltStore ) GetSessionByID (id ID ) (* Session , error ) {
303416 var session * Session
304417 err := db .View (func (tx * bbolt.Tx ) error {
305418 sessionBucket , err := getBucket (tx , sessionBucketKey )
@@ -337,7 +450,7 @@ func (db *DB) GetSessionByID(id ID) (*Session, error) {
337450// used or discarded.
338451//
339452// NOTE: this is part of the Store interface.
340- func (db * DB ) GetUnusedIDAndKeyPair () (ID , * btcec.PrivateKey , error ) {
453+ func (db * BoltStore ) GetUnusedIDAndKeyPair () (ID , * btcec.PrivateKey , error ) {
341454 var (
342455 id ID
343456 privKey * btcec.PrivateKey
@@ -383,7 +496,7 @@ func (db *DB) GetUnusedIDAndKeyPair() (ID, *btcec.PrivateKey, error) {
383496// GetGroupID will return the group ID for the given session ID.
384497//
385498// NOTE: this is part of the IDToGroupIndex interface.
386- func (db * DB ) GetGroupID (sessionID ID ) (ID , error ) {
499+ func (db * BoltStore ) GetGroupID (sessionID ID ) (ID , error ) {
387500 var groupID ID
388501 err := db .View (func (tx * bbolt.Tx ) error {
389502 sessionBkt , err := getBucket (tx , sessionBucketKey )
@@ -423,7 +536,7 @@ func (db *DB) GetGroupID(sessionID ID) (ID, error) {
423536// group with the given ID.
424537//
425538// NOTE: this is part of the IDToGroupIndex interface.
426- func (db * DB ) GetSessionIDs (groupID ID ) ([]ID , error ) {
539+ func (db * BoltStore ) GetSessionIDs (groupID ID ) ([]ID , error ) {
427540 var (
428541 sessionIDs []ID
429542 err error
@@ -450,7 +563,7 @@ func (db *DB) GetSessionIDs(groupID ID) ([]ID, error) {
450563// each session passes.
451564//
452565// NOTE: this is part of the Store interface.
453- func (db * DB ) CheckSessionGroupPredicate (groupID ID ,
566+ func (db * BoltStore ) CheckSessionGroupPredicate (groupID ID ,
454567 fn func (s * Session ) bool ) (bool , error ) {
455568
456569 var (
0 commit comments