@@ -2,8 +2,11 @@ package session
2
2
3
3
import (
4
4
"bytes"
5
+ "encoding/binary"
5
6
"errors"
6
7
"fmt"
8
+ "os"
9
+ "path/filepath"
7
10
"time"
8
11
9
12
"github.com/btcsuite/btcd/btcec/v2"
@@ -46,15 +49,125 @@ var (
46
49
// IDs associated with the given group ID.
47
50
sessionIDKey = []byte ("session-id" )
48
51
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
-
53
52
// ErrDBInitErr is returned when a bucket that we expect to have been
54
53
// set up during DB initialisation is not found.
55
54
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
56
75
)
57
76
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
+
58
171
// getSessionKey returns the key for a session.
59
172
func getSessionKey (session * Session ) []byte {
60
173
return session .LocalPublicKey .SerializeCompressed ()
@@ -64,7 +177,7 @@ func getSessionKey(session *Session) []byte {
64
177
// local public key already exists an error is returned.
65
178
//
66
179
// NOTE: this is part of the Store interface.
67
- func (db * DB ) CreateSession (session * Session ) error {
180
+ func (db * BoltStore ) CreateSession (session * Session ) error {
68
181
var buf bytes.Buffer
69
182
if err := SerializeSession (& buf , session ); err != nil {
70
183
return err
@@ -158,7 +271,7 @@ func (db *DB) CreateSession(session *Session) error {
158
271
// to the session with the given local pub key.
159
272
//
160
273
// NOTE: this is part of the Store interface.
161
- func (db * DB ) UpdateSessionRemotePubKey (localPubKey ,
274
+ func (db * BoltStore ) UpdateSessionRemotePubKey (localPubKey ,
162
275
remotePubKey * btcec.PublicKey ) error {
163
276
164
277
key := localPubKey .SerializeCompressed ()
@@ -196,7 +309,7 @@ func (db *DB) UpdateSessionRemotePubKey(localPubKey,
196
309
// GetSession fetches the session with the given key.
197
310
//
198
311
// 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 ) {
200
313
var session * Session
201
314
err := db .View (func (tx * bbolt.Tx ) error {
202
315
sessionBucket , err := getBucket (tx , sessionBucketKey )
@@ -226,7 +339,7 @@ func (db *DB) GetSession(key *btcec.PublicKey) (*Session, error) {
226
339
// ListSessions returns all sessions currently known to the store.
227
340
//
228
341
// 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 ) {
230
343
var sessions []* Session
231
344
err := db .View (func (tx * bbolt.Tx ) error {
232
345
sessionBucket , err := getBucket (tx , sessionBucketKey )
@@ -266,7 +379,7 @@ func (db *DB) ListSessions(filterFn func(s *Session) bool) ([]*Session, error) {
266
379
// public key to be revoked.
267
380
//
268
381
// 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 {
270
383
var session * Session
271
384
return db .Update (func (tx * bbolt.Tx ) error {
272
385
sessionBucket , err := getBucket (tx , sessionBucketKey )
@@ -299,7 +412,7 @@ func (db *DB) RevokeSession(key *btcec.PublicKey) error {
299
412
// GetSessionByID fetches the session with the given ID.
300
413
//
301
414
// 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 ) {
303
416
var session * Session
304
417
err := db .View (func (tx * bbolt.Tx ) error {
305
418
sessionBucket , err := getBucket (tx , sessionBucketKey )
@@ -337,7 +450,7 @@ func (db *DB) GetSessionByID(id ID) (*Session, error) {
337
450
// used or discarded.
338
451
//
339
452
// 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 ) {
341
454
var (
342
455
id ID
343
456
privKey * btcec.PrivateKey
@@ -383,7 +496,7 @@ func (db *DB) GetUnusedIDAndKeyPair() (ID, *btcec.PrivateKey, error) {
383
496
// GetGroupID will return the group ID for the given session ID.
384
497
//
385
498
// 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 ) {
387
500
var groupID ID
388
501
err := db .View (func (tx * bbolt.Tx ) error {
389
502
sessionBkt , err := getBucket (tx , sessionBucketKey )
@@ -423,7 +536,7 @@ func (db *DB) GetGroupID(sessionID ID) (ID, error) {
423
536
// group with the given ID.
424
537
//
425
538
// 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 ) {
427
540
var (
428
541
sessionIDs []ID
429
542
err error
@@ -450,7 +563,7 @@ func (db *DB) GetSessionIDs(groupID ID) ([]ID, error) {
450
563
// each session passes.
451
564
//
452
565
// NOTE: this is part of the Store interface.
453
- func (db * DB ) CheckSessionGroupPredicate (groupID ID ,
566
+ func (db * BoltStore ) CheckSessionGroupPredicate (groupID ID ,
454
567
fn func (s * Session ) bool ) (bool , error ) {
455
568
456
569
var (
0 commit comments