// loadManager returns a new stake manager that results from loading it from // the passed opened database. The public passphrase is required to decrypt the // public keys. func (s *StakeStore) loadOwnedSStxs(namespace walletdb.Namespace) error { // Regenerate the list of tickets. // Perform all database lookups in a read-only view. ticketList := make(map[chainhash.Hash]struct{}) err := namespace.View(func(tx walletdb.Tx) error { var errForEach error // Open the sstx records database. bucket := tx.RootBucket().Bucket(sstxRecordsBucketName) // Store each key sequentially. errForEach = bucket.ForEach(func(k []byte, v []byte) error { var errNewHash error var hash *chainhash.Hash hash, errNewHash = chainhash.NewHash(k) if errNewHash != nil { return errNewHash } ticketList[*hash] = struct{}{} return nil }) return errForEach }) if err != nil { return err } s.ownedSStxs = ticketList return nil }
// Create creates a new entry in the database with the given ID // and returns the Pool representing it. func Create(namespace walletdb.Namespace, m *waddrmgr.Manager, poolID []byte) (*Pool, error) { err := namespace.Update( func(tx walletdb.Tx) error { return putPool(tx, poolID) }) if err != nil { str := fmt.Sprintf("unable to add voting pool %v to db", poolID) return nil, newError(ErrPoolAlreadyExists, str, err) } return newPool(namespace, m, poolID), nil }
// stakeStoreExists returns whether or not the stake store has already // been created in the given database namespace. func stakeStoreExists(namespace walletdb.Namespace) (bool, error) { var exists bool err := namespace.View(func(tx walletdb.Tx) error { mainBucket := tx.RootBucket().Bucket(mainBucketName) exists = mainBucket != nil return nil }) if err != nil { str := fmt.Sprintf("failed to obtain database view: %v", err) return false, stakeStoreError(ErrDatabase, str, err) } return exists, nil }
// Load fetches the entry in the database with the given ID and returns the Pool // representing it. func Load(namespace walletdb.Namespace, m *waddrmgr.Manager, poolID []byte) (*Pool, error) { err := namespace.View( func(tx walletdb.Tx) error { if exists := existsPool(tx, poolID); !exists { str := fmt.Sprintf("unable to find voting pool %v in db", poolID) return newError(ErrPoolNotExists, str, nil) } return nil }) if err != nil { return nil, err } p := newPool(namespace, m, poolID) if err = p.LoadAllSeries(); err != nil { return nil, err } return p, nil }
// testManualTxInterface ensures that manual transactions work as expected. func testManualTxInterface(tc *testContext, namespace walletdb.Namespace) bool { // populateValues tests that populating values works as expected. // // When the writable flag is false, a read-only tranasction is created, // standard bucket tests for read-only transactions are performed, and // the Commit function is checked to ensure it fails as expected. // // Otherwise, a read-write transaction is created, the values are // written, standard bucket tests for read-write transactions are // performed, and then the transaction is either commited or rolled // back depending on the flag. populateValues := func(writable, rollback bool, putValues map[string]string) bool { tx, err := namespace.Begin(writable) if err != nil { tc.t.Errorf("Begin: unexpected error %v", err) return false } rootBucket := tx.RootBucket() if rootBucket == nil { tc.t.Errorf("RootBucket: unexpected nil root bucket") _ = tx.Rollback() return false } tc.isWritable = writable if !testBucketInterface(tc, rootBucket) { _ = tx.Rollback() return false } if !writable { // The transaction is not writable, so it should fail // the commit. if err := tx.Commit(); err != walletdb.ErrTxNotWritable { tc.t.Errorf("Commit: unexpected error %v, "+ "want %v", err, walletdb.ErrTxNotWritable) _ = tx.Rollback() return false } // Rollback the transaction. if err := tx.Rollback(); err != nil { tc.t.Errorf("Commit: unexpected error %v", err) return false } } else { if !testPutValues(tc, rootBucket, putValues) { return false } if rollback { // Rollback the transaction. if err := tx.Rollback(); err != nil { tc.t.Errorf("Rollback: unexpected "+ "error %v", err) return false } } else { // The commit should succeed. if err := tx.Commit(); err != nil { tc.t.Errorf("Commit: unexpected error "+ "%v", err) return false } } } return true } // checkValues starts a read-only transaction and checks that all of // the key/value pairs specified in the expectedValues parameter match // what's in the database. checkValues := func(expectedValues map[string]string) bool { // Begin another read-only transaction to ensure... tx, err := namespace.Begin(false) if err != nil { tc.t.Errorf("Begin: unexpected error %v", err) return false } rootBucket := tx.RootBucket() if rootBucket == nil { tc.t.Errorf("RootBucket: unexpected nil root bucket") _ = tx.Rollback() return false } if !testGetValues(tc, rootBucket, expectedValues) { _ = tx.Rollback() return false } // Rollback the read-only transaction. if err := tx.Rollback(); err != nil { tc.t.Errorf("Commit: unexpected error %v", err) return false } return true } // deleteValues starts a read-write transaction and deletes the keys // in the passed key/value pairs. deleteValues := func(values map[string]string) bool { tx, err := namespace.Begin(true) if err != nil { } rootBucket := tx.RootBucket() if rootBucket == nil { tc.t.Errorf("RootBucket: unexpected nil root bucket") _ = tx.Rollback() return false } // Delete the keys and ensure they were deleted. if !testDeleteValues(tc, rootBucket, values) { _ = tx.Rollback() return false } if !testGetValues(tc, rootBucket, rollbackValues(values)) { _ = tx.Rollback() return false } // Commit the changes and ensure it was successful. if err := tx.Commit(); err != nil { tc.t.Errorf("Commit: unexpected error %v", err) return false } return true } // keyValues holds the keys and values to use when putting values // into a bucket. var keyValues = map[string]string{ "umtxkey1": "foo1", "umtxkey2": "foo2", "umtxkey3": "foo3", } // Ensure that attempting populating the values using a read-only // transaction fails as expected. if !populateValues(false, true, keyValues) { return false } if !checkValues(rollbackValues(keyValues)) { return false } // Ensure that attempting populating the values using a read-write // transaction and then rolling it back yields the expected values. if !populateValues(true, true, keyValues) { return false } if !checkValues(rollbackValues(keyValues)) { return false } // Ensure that attempting populating the values using a read-write // transaction and then committing it stores the expected values. if !populateValues(true, false, keyValues) { return false } if !checkValues(keyValues) { return false } // Clean up the keys. if !deleteValues(keyValues) { return false } return true }
// initialize creates the DB if it doesn't exist, and otherwise // loads the database. func initializeEmpty(namespace walletdb.Namespace) error { // Initialize the buckets and main db fields as needed. var version uint32 var createDate uint64 err := namespace.Update(func(tx walletdb.Tx) error { rootBucket := tx.RootBucket() mainBucket, err := rootBucket.CreateBucketIfNotExists( mainBucketName) if err != nil { str := "failed to create main bucket" return stakeStoreError(ErrDatabase, str, err) } _, err = rootBucket.CreateBucketIfNotExists(sstxRecordsBucketName) if err != nil { str := "failed to create sstx records bucket" return stakeStoreError(ErrDatabase, str, err) } _, err = rootBucket.CreateBucketIfNotExists(ssgenRecordsBucketName) if err != nil { str := "failed to create ssgen records bucket" return stakeStoreError(ErrDatabase, str, err) } _, err = rootBucket.CreateBucketIfNotExists(ssrtxRecordsBucketName) if err != nil { str := "failed to create ssrtx records bucket" return stakeStoreError(ErrDatabase, str, err) } _, err = rootBucket.CreateBucketIfNotExists(metaBucketName) if err != nil { str := "failed to create meta bucket" return stakeStoreError(ErrDatabase, str, err) } // Save the most recent tx store version if it isn't already // there, otherwise keep track of it for potential upgrades. verBytes := mainBucket.Get(stakeStoreVersionName) if verBytes == nil { version = LatestStakeMgrVersion var buf [4]byte byteOrder.PutUint32(buf[:], version) err := mainBucket.Put(stakeStoreVersionName, buf[:]) if err != nil { str := "failed to store latest database version" return stakeStoreError(ErrDatabase, str, err) } } else { version = byteOrder.Uint32(verBytes) } createBytes := mainBucket.Get(stakeStoreCreateDateName) if createBytes == nil { createDate = uint64(time.Now().Unix()) var buf [8]byte byteOrder.PutUint64(buf[:], createDate) err := mainBucket.Put(stakeStoreCreateDateName, buf[:]) if err != nil { str := "failed to store database creation time" return stakeStoreError(ErrDatabase, str, err) } } else { createDate = byteOrder.Uint64(createBytes) } return nil }) if err != nil { str := "failed to load database" return stakeStoreError(ErrDatabase, str, err) } return nil }