func (db *BoltDB) persistJob(job *Job) error {
	const funcName = "persistJob"
	return db.DB.Update(func(tx *bolt.Tx) error {
		bkt, err := fetchBucket(tx, jobBkt)
		if err != nil {
			return err
		}
		if bkt.Get([]byte(job.UUID)) != nil {
			desc := fmt.Sprintf("%s: job %s already exists", funcName,
				job.UUID)
			return errs.DBError(errs.ValueFound, desc)
		}
		jobBytes, err := json.Marshal(job)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to marshal job bytes: %v",
				funcName, err)
			return errs.DBError(errs.Parse, desc)
		}
		err = bkt.Put([]byte(job.UUID), jobBytes)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to persist job entry: %v",
				funcName, err)
			return errs.DBError(errs.PersistEntry, desc)
		}
		return nil
	})
}
persistJob saves the job to the database.
package pool
import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"net/http"
	"path/filepath"
	"strconv"
	"time"
	bolt "go.etcd.io/bbolt"
	errs "github.com/decred/dcrpool/errors"
)
var (
	poolBkt = []byte("poolbkt")
	accountBkt = []byte("accountbkt")
	shareBkt = []byte("sharebkt")
	jobBkt = []byte("jobbkt")
	workBkt = []byte("workbkt")
	paymentBkt = []byte("paymentbkt")
	paymentArchiveBkt = []byte("paymentarchivebkt")
	hashDataBkt = []byte("hashdatabkt")
	versionK = []byte("version")
	lastPaymentCreatedOn = []byte("lastpaymentcreatedon")
	lastPaymentPaidOn = []byte("lastpaymentpaidon")
	lastPaymentHeight = []byte("lastpaymentheight")
	soloPool = []byte("solopool")
	csrfSecret = []byte("csrfsecret")
	PoolFeesK = "fees"
	BoltBackupFile = "backup.kv"
)
func openBoltDB(storage string) (*BoltDB, error) {
	const funcName = "openBoltDB"
	db, err := bolt.Open(storage, 0600,
		&bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		desc := fmt.Sprintf("%s: unable to open db file: %v", funcName, err)
		return nil, errs.DBError(errs.DBOpen, desc)
	}
	return &BoltDB{db}, nil
}
func createNestedBucket(parent *bolt.Bucket, child []byte) error {
	const funcName = "createNestedBucket"
	_, err := parent.CreateBucketIfNotExists(child)
	if err != nil {
		desc := fmt.Sprintf("%s: unable to create %s bucket: %v",
			funcName, string(child), err)
		return errs.DBError(errs.CreateStorage, desc)
	}
	return nil
}
func createBuckets(db *BoltDB) error {
	const funcName = "createBuckets"
	err := db.DB.Update(func(tx *bolt.Tx) error {
		var err error
		pbkt := tx.Bucket(poolBkt)
		if pbkt == nil {
			pbkt, err = tx.CreateBucketIfNotExists(poolBkt)
			if err != nil {
				desc := fmt.Sprintf("%s: unable to create %s bucket: %v",
					funcName, string(poolBkt), err)
				return errs.DBError(errs.CreateStorage, desc)
			}
			vbytes := make([]byte, 4)
			binary.LittleEndian.PutUint32(vbytes, BoltDBVersion)
			err = pbkt.Put(versionK, vbytes)
			if err != nil {
				desc := fmt.Sprintf("%s: unable to persist version: %v",
					funcName, err)
				return errs.DBError(errs.PersistEntry, desc)
			}
		}
		err = createNestedBucket(pbkt, accountBkt)
		if err != nil {
			return err
		}
		err = createNestedBucket(pbkt, shareBkt)
		if err != nil {
			return err
		}
		err = createNestedBucket(pbkt, workBkt)
		if err != nil {
			return err
		}
		err = createNestedBucket(pbkt, jobBkt)
		if err != nil {
			return err
		}
		err = createNestedBucket(pbkt, paymentBkt)
		if err != nil {
			return err
		}
		err = createNestedBucket(pbkt, paymentArchiveBkt)
		if err != nil {
			return err
		}
		return createNestedBucket(pbkt, hashDataBkt)
	})
	return err
}
func (db *BoltDB) Backup(backupFileName string) error {
	backupPath := filepath.Join(filepath.Dir(db.DB.Path()), backupFileName)
	return db.DB.View(func(tx *bolt.Tx) error {
		err := tx.CopyFile(backupPath, 0600)
		if err != nil {
			desc := fmt.Sprintf("unable to backup db: %v", err)
			return errs.PoolError(errs.Backup, desc)
		}
		return nil
	})
}
func InitBoltDB(dbFile string) (*BoltDB, error) {
	db, err := openBoltDB(dbFile)
	if err != nil {
		return nil, err
	}
	err = createBuckets(db)
	if err != nil {
		return nil, err
	}
	err = upgradeDB(db)
	if err != nil {
		return nil, err
	}
	return db, nil
}
func deleteEntry(db *BoltDB, bucket []byte, key string) error {
	const funcName = "deleteEntry"
	return db.DB.Update(func(tx *bolt.Tx) error {
		pbkt := tx.Bucket(poolBkt)
		if pbkt == nil {
			desc := fmt.Sprintf("%s: bucket %s not found", funcName,
				string(poolBkt))
			return errs.DBError(errs.StorageNotFound, desc)
		}
		b := pbkt.Bucket(bucket)
		err := b.Delete([]byte(key))
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete entry with "+
				"key %s from bucket %s", funcName, key, string(poolBkt))
			return errs.DBError(errs.DeleteEntry, desc)
		}
		return nil
	})
}
func fetchBucket(tx *bolt.Tx, bucketID []byte) (*bolt.Bucket, error) {
	const funcName = "fetchBucket"
	pbkt, err := fetchPoolBucket(tx)
	if err != nil {
		return nil, err
	}
	bkt := pbkt.Bucket(bucketID)
	if bkt == nil {
		desc := fmt.Sprintf("%s: bucket %s not found", funcName,
			string(bucketID))
		return nil, errs.DBError(errs.StorageNotFound, desc)
	}
	return bkt, nil
}
func fetchPoolBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
	funcName := "fetchPoolBucket"
	pbkt := tx.Bucket(poolBkt)
	if pbkt == nil {
		desc := fmt.Sprintf("%s: bucket %s not found", funcName,
			string(poolBkt))
		return nil, errs.DBError(errs.StorageNotFound, desc)
	}
	return pbkt, nil
}
func bigEndianBytesToNano(b []byte) uint64 {
	return binary.BigEndian.Uint64(b)
}
func nanoToBigEndianBytes(nano int64) []byte {
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, uint64(nano))
	return b
}
func (db *BoltDB) fetchPoolMode() (uint32, error) {
	var mode uint32
	err := db.DB.View(func(tx *bolt.Tx) error {
		pbkt, err := fetchPoolBucket(tx)
		if err != nil {
			return err
		}
		b := pbkt.Get(soloPool)
		if b == nil {
			return errs.DBError(errs.ValueNotFound, "no pool mode found")
		}
		mode = binary.LittleEndian.Uint32(b)
		return nil
	})
	if err != nil {
		return 0, err
	}
	return mode, nil
}
func (db *BoltDB) persistPoolMode(mode uint32) error {
	return db.DB.Update(func(tx *bolt.Tx) error {
		pbkt := tx.Bucket(poolBkt)
		b := make([]byte, 4)
		binary.LittleEndian.PutUint32(b, mode)
		return pbkt.Put(soloPool, b)
	})
}
func (db *BoltDB) fetchCSRFSecret() ([]byte, error) {
	var secret []byte
	err := db.DB.View(func(tx *bolt.Tx) error {
		pbkt := tx.Bucket(poolBkt)
		if pbkt == nil {
			desc := fmt.Sprintf("bucket %s not found", string(poolBkt))
			return errs.DBError(errs.StorageNotFound, desc)
		}
		v := pbkt.Get(csrfSecret)
		if v == nil {
			return errs.DBError(errs.ValueNotFound, "No csrf secret found")
		}
		secret = make([]byte, len(v))
		copy(secret, v)
		return nil
	})
	if err != nil {
		return nil, err
	}
	return secret, nil
}
func (db *BoltDB) persistCSRFSecret(secret []byte) error {
	return db.DB.Update(func(tx *bolt.Tx) error {
		pbkt := tx.Bucket(poolBkt)
		if pbkt == nil {
			desc := fmt.Sprintf("bucket %s not found", string(poolBkt))
			return errs.DBError(errs.StorageNotFound, desc)
		}
		return pbkt.Put(csrfSecret, secret)
	})
}
func (db *BoltDB) persistLastPaymentInfo(height uint32, paidOn int64) error {
	funcName := "persistLastPaymentInfo"
	return db.DB.Update(func(tx *bolt.Tx) error {
		pbkt, err := fetchPoolBucket(tx)
		if err != nil {
			return err
		}
		b := make([]byte, 4)
		binary.LittleEndian.PutUint32(b, height)
		err = pbkt.Put(lastPaymentHeight, b)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to persist last payment height: %v",
				funcName, err)
			return errs.DBError(errs.PersistEntry, desc)
		}
		err = pbkt.Put(lastPaymentPaidOn, nanoToBigEndianBytes(paidOn))
		if err != nil {
			desc := fmt.Sprintf("%s: unable to persist last payment "+
				"paid on time: %v", funcName, err)
			return errs.DBError(errs.PersistEntry, desc)
		}
		return nil
	})
}
func (db *BoltDB) loadLastPaymentInfo() (uint32, int64, error) {
	funcName := "loadLastPaymentInfo"
	var height uint32
	var paidOn int64
	err := db.DB.View(func(tx *bolt.Tx) error {
		pbkt, err := fetchPoolBucket(tx)
		if err != nil {
			return err
		}
		lastPaymentHeightB := pbkt.Get(lastPaymentHeight)
		lastPaymentPaidOnB := pbkt.Get(lastPaymentPaidOn)
		if lastPaymentHeightB == nil || lastPaymentPaidOnB == nil {
			desc := fmt.Sprintf("%s: last payment info not initialized", funcName)
			return errs.DBError(errs.ValueNotFound, desc)
		}
		height = binary.LittleEndian.Uint32(lastPaymentHeightB)
		paidOn = int64(bigEndianBytesToNano(lastPaymentPaidOnB))
		return nil
	})
	if err != nil {
		return 0, 0, err
	}
	return height, paidOn, nil
}
func (db *BoltDB) persistLastPaymentCreatedOn(createdOn int64) error {
	funcName := "persistLastPaymentCreatedOn"
	return db.DB.Update(func(tx *bolt.Tx) error {
		pbkt, err := fetchPoolBucket(tx)
		if err != nil {
			return err
		}
		err = pbkt.Put(lastPaymentCreatedOn, nanoToBigEndianBytes(createdOn))
		if err != nil {
			desc := fmt.Sprintf("%s: unable to persist last payment "+
				"created-on time: %v", funcName, err)
			return errs.DBError(errs.PersistEntry, desc)
		}
		return nil
	})
}
func (db *BoltDB) loadLastPaymentCreatedOn() (int64, error) {
	funcName := "loadLastPaymentCreatedOn"
	var createdOn int64
	err := db.DB.View(func(tx *bolt.Tx) error {
		pbkt, err := fetchPoolBucket(tx)
		if err != nil {
			return err
		}
		lastPaymentCreatedOnB := pbkt.Get(lastPaymentCreatedOn)
		if lastPaymentCreatedOnB == nil {
			desc := fmt.Sprintf("%s: last payment created-on not initialized",
				funcName)
			return errs.DBError(errs.ValueNotFound, desc)
		}
		createdOn = int64(bigEndianBytesToNano(lastPaymentCreatedOnB))
		return nil
	})
	if err != nil {
		return 0, err
	}
	return createdOn, nil
}
func (db *BoltDB) Close() error {
	return db.DB.Close()
}
func (db *BoltDB) purge() error {
	funcName := "purge"
	return db.DB.Update(func(tx *bolt.Tx) error {
		pbkt, err := fetchPoolBucket(tx)
		if err != nil {
			return err
		}
		err = pbkt.Delete(csrfSecret)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete csrf secret: %v",
				funcName, err)
			return errs.DBError(errs.DeleteEntry, desc)
		}
		err = pbkt.Delete(soloPool)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete solo pool status: %v",
				funcName, err)
			return errs.DBError(errs.DeleteEntry, desc)
		}
		err = pbkt.Delete(lastPaymentHeight)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete last payment "+
				"height: %v", funcName, err)
			return errs.DBError(errs.DeleteEntry, desc)
		}
		err = pbkt.Delete(lastPaymentPaidOn)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete last payment "+
				"paid on: %v", funcName, err)
			return errs.DBError(errs.DeleteEntry, desc)
		}
		err = pbkt.Delete(lastPaymentCreatedOn)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete last payment "+
				"created on: %v", funcName, err)
			return errs.DBError(errs.DeleteEntry, desc)
		}
		err = pbkt.Delete(versionK)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete db "+
				"version: %v", funcName, err)
			return errs.DBError(errs.DeleteEntry, desc)
		}
		err = pbkt.DeleteBucket(paymentArchiveBkt)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete payment archive "+
				"bucket: %v", funcName, err)
			return errs.DBError(errs.DeleteEntry, desc)
		}
		err = pbkt.DeleteBucket(paymentBkt)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete payment bucket: %v",
				funcName, err)
			return errs.DBError(errs.DeleteEntry, desc)
		}
		err = pbkt.DeleteBucket(workBkt)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete work bucket: %v",
				funcName, err)
			return errs.DBError(errs.DeleteEntry, desc)
		}
		err = pbkt.DeleteBucket(jobBkt)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete job bucket: %v",
				funcName, err)
			return errs.DBError(errs.DeleteEntry, desc)
		}
		err = pbkt.DeleteBucket(shareBkt)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete share bucket: %v",
				funcName, err)
			return errs.DBError(errs.DeleteEntry, desc)
		}
		err = pbkt.DeleteBucket(accountBkt)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to delete account bucket: %v",
				funcName, err)
			return errs.DBError(errs.DeleteEntry, desc)
		}
		return nil
	})
}
func (db *BoltDB) httpBackup(w http.ResponseWriter) error {
	err := db.DB.View(func(tx *bolt.Tx) error {
		w.Header().Set("Content-Type", "application/octet-stream")
		w.Header().Set("Content-Disposition", `attachment; filename="backup.db"`)
		w.Header().Set("Content-Length", strconv.Itoa(int(tx.Size())))
		_, err := tx.WriteTo(w)
		return err
	})
	return err
}
func (db *BoltDB) fetchAcceptedWork(id string) (*AcceptedWork, error) {
	const funcName = "fetchAcceptedWork"
	var work AcceptedWork
	err := db.DB.View(func(tx *bolt.Tx) error {
		bkt, err := fetchBucket(tx, workBkt)
		if err != nil {
			return err
		}
		v := bkt.Get([]byte(id))
		if v == nil {
			desc := fmt.Sprintf("%s: no value for key %s", funcName, id)
			return errs.DBError(errs.ValueNotFound, desc)
		}
		err = json.Unmarshal(v, &work)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to unmarshal accepted work: %v",
				funcName, err)
			return errs.DBError(errs.Parse, desc)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return &work, err
}
func (db *BoltDB) persistAcceptedWork(work *AcceptedWork) error {
	const funcName = "persistAcceptedWork"
	return db.DB.Update(func(tx *bolt.Tx) error {
		bkt, err := fetchBucket(tx, workBkt)
		if err != nil {
			return err
		}
		id := []byte(work.UUID)
		v := bkt.Get(id)
		if v != nil {
			desc := fmt.Sprintf("%s: work %s already exists", funcName,
				work.UUID)
			return errs.DBError(errs.ValueFound, desc)
		}
		workBytes, err := json.Marshal(work)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to marshal accepted "+
				"work bytes: %v", funcName, err)
			return errs.DBError(errs.Parse, desc)
		}
		err = bkt.Put(id, workBytes)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to persist accepted work: %v",
				funcName, err)
			return errs.DBError(errs.PersistEntry, desc)
		}
		return nil
	})
}
func (db *BoltDB) updateAcceptedWork(work *AcceptedWork) error {
	const funcName = "updateAcceptedWork"
	return db.DB.Update(func(tx *bolt.Tx) error {
		bkt, err := fetchBucket(tx, workBkt)
		if err != nil {
			return err
		}
		id := []byte(work.UUID)
		v := bkt.Get(id)
		if v == nil {
			desc := fmt.Sprintf("%s: work %s not found", funcName, work.UUID)
			return errs.DBError(errs.ValueNotFound, desc)
		}
		workBytes, err := json.Marshal(work)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to marshal accepted "+
				"work bytes: %v", funcName, err)
			return errs.DBError(errs.PersistEntry, desc)
		}
		err = bkt.Put(id, workBytes)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to persist accepted work: %v",
				funcName, err)
			return errs.DBError(errs.PersistEntry, desc)
		}
		return nil
	})
}
func (db *BoltDB) deleteAcceptedWork(id string) error {
	return deleteEntry(db, workBkt, id)
}
func (db *BoltDB) listMinedWork() ([]*AcceptedWork, error) {
	const funcName = "listMinedWork"
	minedWork := make([]*AcceptedWork, 0)
	err := db.DB.View(func(tx *bolt.Tx) error {
		bkt, err := fetchBucket(tx, workBkt)
		if err != nil {
			return err
		}
		cursor := bkt.Cursor()
		for k, v := cursor.Last(); k != nil; k, v = cursor.Prev() {
			var work AcceptedWork
			err := json.Unmarshal(v, &work)
			if err != nil {
				desc := fmt.Sprintf("%s: unable to unmarshal accepted "+
					"work: %v", funcName, err)
				return errs.PoolError(errs.Parse, desc)
			}
			minedWork = append(minedWork, &work)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return minedWork, nil
}
func (db *BoltDB) fetchUnconfirmedWork(height uint32) ([]*AcceptedWork, error) {
	toReturn := make([]*AcceptedWork, 0)
	err := db.DB.View(func(tx *bolt.Tx) error {
		bkt, err := fetchBucket(tx, workBkt)
		if err != nil {
			return err
		}
		heightBE := heightToBigEndianBytes(height)
		cursor := bkt.Cursor()
		for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
			heightB, err := hex.DecodeString(string(k[:8]))
			if err != nil {
				return err
			}
			if bytes.Compare(heightBE, heightB) > 0 {
				var work AcceptedWork
				err := json.Unmarshal(v, &work)
				if err != nil {
					return err
				}
				if !work.Confirmed {
					toReturn = append(toReturn, &work)
				}
			}
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return toReturn, nil
}
func (db *BoltDB) fetchAccount(id string) (*Account, error) {
	const funcName = "fetchAccount"
	var account Account
	err := db.DB.View(func(tx *bolt.Tx) error {
		bkt, err := fetchBucket(tx, accountBkt)
		if err != nil {
			return err
		}
		v := bkt.Get([]byte(id))
		if v == nil {
			desc := fmt.Sprintf("%s: no account found for id %s", funcName, id)
			return errs.DBError(errs.ValueNotFound, desc)
		}
		err = json.Unmarshal(v, &account)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to unmarshal account: %v",
				funcName, err)
			return errs.DBError(errs.Parse, desc)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return &account, err
}
func (db *BoltDB) persistAccount(acc *Account) error {
	const funcName = "persistAccount"
	return db.DB.Update(func(tx *bolt.Tx) error {
		bkt, err := fetchBucket(tx, accountBkt)
		if err != nil {
			return err
		}
		if bkt.Get([]byte(acc.UUID)) != nil {
			desc := fmt.Sprintf("%s: account %s already exists", funcName,
				acc.UUID)
			return errs.DBError(errs.ValueFound, desc)
		}
		acc.CreatedOn = uint64(time.Now().Unix())
		accBytes, err := json.Marshal(acc)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to marshal account bytes: %v",
				funcName, err)
			return errs.DBError(errs.Parse, desc)
		}
		err = bkt.Put([]byte(acc.UUID), accBytes)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to persist account entry: %v",
				funcName, err)
			return errs.DBError(errs.PersistEntry, desc)
		}
		return nil
	})
}
func (db *BoltDB) deleteAccount(id string) error {
	return deleteEntry(db, accountBkt, id)
}
func (db *BoltDB) fetchJob(id string) (*Job, error) {
	const funcName = "fetchJob"
	var job Job
	err := db.DB.View(func(tx *bolt.Tx) error {
		bkt, err := fetchBucket(tx, jobBkt)
		if err != nil {
			return err
		}
		v := bkt.Get([]byte(id))
		if v == nil {
			desc := fmt.Sprintf("%s: no job found for id %s", funcName, id)
			return errs.DBError(errs.ValueNotFound, desc)
		}
		err = json.Unmarshal(v, &job)
		if err != nil {
			desc := fmt.Sprintf("%s: unable to unmarshal job bytes: %v",
				funcName, err)
			return errs.DBError(errs.Parse, desc)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return &job, err
}