func (s *BoltDBStore) Addresses() []types.UnlockHash {
	addrs := make([]types.UnlockHash, 0, len(s.addrs))
	for addr := range s.addrs {
		addrs = append(addrs, addr)
	}
	return addrs
}
Addresses implements Store.
package wallet
import (
	"bytes"
	"encoding/binary"
	"os"
	"time"
	"go.sia.tech/siad/modules"
	"go.sia.tech/siad/types"
	"gitlab.com/NebulousLabs/encoding"
	bolt "go.etcd.io/bbolt"
)
var (
	keyHeight = []byte("keyHeight")
	keyCCID = []byte("keyCCID")
	keySeedIndex = []byte("keySeedIndex")
	bucketMeta = []byte("bucketMeta")
	bucketAddrs = []byte("bucketAddrs")
	bucketOutputs = []byte("bucketOutputs")
	bucketBlockRewards = []byte("bucketBlockRewards")
	bucketFileContracts = []byte("bucketFileContracts")
	bucketMemos = []byte("bucketMemos")
	bucketTxns = []byte("bucketTxns")
	bucketTxnsAddrIndex = []byte("bucketTxnsAddrIndex")
	bucketTxnsRecentIndex = []byte("bucketTxnsRecentIndex")
	bucketLimbo = []byte("bucketLimbo")
	dbBuckets = [][]byte{
		bucketAddrs,
		bucketBlockRewards,
		bucketFileContracts,
		bucketLimbo,
		bucketMemos,
		bucketMeta,
		bucketOutputs,
		bucketTxns,
		bucketTxnsAddrIndex,
		bucketTxnsRecentIndex,
	}
)
type BoltDBStore struct {
	db    *bolt.DB
	addrs map[types.UnlockHash]struct{}
	onErr func(error)
}
func (s *BoltDBStore) view(fn func(*bolt.Tx) error) {
	err := s.db.View(fn)
	if err != nil {
		s.onErr(err)
	}
}
func (s *BoltDBStore) update(fn func(*bolt.Tx) error) {
	err := s.db.Update(fn)
	if err != nil {
		s.onErr(err)
	}
}
func (s *BoltDBStore) ApplyConsensusChange(reverted, applied ProcessedConsensusChange, ccid modules.ConsensusChangeID) {
	s.update(func(tx *bolt.Tx) error {
		for _, o := range reverted.Outputs {
			tx.Bucket(bucketOutputs).Delete(o.ID[:])
		}
		if len(reverted.BlockRewards) > 0 {
			for i := range reverted.BlockRewards {
				c := tx.Bucket(bucketBlockRewards).Cursor()
				for k, v := c.Last(); k != nil; k, v = c.Prev() {
					var br BlockReward
					encoding.Unmarshal(v, &br)
					if br.ID == reverted.BlockRewards[i].ID {
						tx.Bucket(bucketBlockRewards).Delete(k)
						break
					}
				}
			}
		}
		if len(reverted.FileContracts) > 0 {
			for i := range reverted.FileContracts {
				c := tx.Bucket(bucketFileContracts).Cursor()
				for k, v := c.Last(); k != nil; k, v = c.Prev() {
					var fc FileContract
					encoding.Unmarshal(v, &fc)
					if fc.ID == reverted.FileContracts[i].ID && fc.RevisionNumber == reverted.FileContracts[i].RevisionNumber {
						tx.Bucket(bucketFileContracts).Delete(k)
						break
					}
				}
			}
		}
		for _, txn := range reverted.Transactions {
			txid := txn.ID()
			tx.Bucket(bucketTxns).Delete(txid[:])
			tx.Bucket(bucketLimbo).Delete(txid[:])
			c := tx.Bucket(bucketTxnsRecentIndex).Cursor()
			for k, v := c.Last(); k != nil; k, v = c.Prev() {
				if bytes.Equal(v, txid[:]) {
					tx.Bucket(bucketTxnsRecentIndex).Delete(k)
					break
				}
			}
		}
		for addr, txids := range reverted.AddressTransactions {
			addrTxnsBucket := tx.Bucket(bucketTxnsAddrIndex).Bucket(addr[:])
			if addrTxnsBucket == nil {
				continue
			}
			c := addrTxnsBucket.Cursor()
			for k, v := c.Last(); k != nil; k, v = c.Prev() {
				for _, txid := range txids {
					if bytes.Equal(v, txid[:]) {
						addrTxnsBucket.Delete(k)
						break
					}
				}
			}
		}
		seqBytes := make([]byte, 8)
		putSeq := func(b *bolt.Bucket, val []byte) error {
			seq, _ := tx.Bucket(bucketBlockRewards).NextSequence()
			binary.BigEndian.PutUint64(seqBytes, seq)
			return b.Put(seqBytes, val)
		}
		for _, o := range applied.Outputs {
			tx.Bucket(bucketOutputs).Put(o.ID[:], encoding.Marshal(o))
		}
		for _, br := range applied.BlockRewards {
			putSeq(tx.Bucket(bucketBlockRewards), encoding.Marshal(br))
		}
		for _, fc := range applied.FileContracts {
			putSeq(tx.Bucket(bucketFileContracts), encoding.Marshal(fc))
		}
		for _, txn := range applied.Transactions {
			txid := txn.ID()
			tx.Bucket(bucketTxns).Put(txid[:], encoding.Marshal(txn))
			tx.Bucket(bucketLimbo).Delete(txid[:])
			putSeq(tx.Bucket(bucketTxnsRecentIndex), txid[:])
		}
		for addr, txids := range applied.AddressTransactions {
			addrTxnsBucket, _ := tx.Bucket(bucketTxnsAddrIndex).CreateBucketIfNotExists(addr[:])
			for _, txid := range txids {
				putSeq(addrTxnsBucket, txid[:])
			}
		}
		heightBytes := append([]byte(nil), tx.Bucket(bucketMeta).Get(keyHeight)...)
		height := binary.LittleEndian.Uint64(heightBytes) + uint64(applied.BlockCount) - uint64(reverted.BlockCount)
		binary.LittleEndian.PutUint64(heightBytes, height)
		tx.Bucket(bucketMeta).Put(keyHeight, heightBytes)
		tx.Bucket(bucketMeta).Put(keyCCID, ccid[:])
		return nil
	})
}
func (s *BoltDBStore) UnspentOutputs() (outputs []UnspentOutput) {
	s.view(func(tx *bolt.Tx) error {
		c := tx.Bucket(bucketOutputs).Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			var o UnspentOutput
			encoding.Unmarshal(v, &o)
			outputs = append(outputs, o)
		}
		return nil
	})
	return
}
func (s *BoltDBStore) AddToLimbo(txn types.Transaction) {
	s.update(func(tx *bolt.Tx) error {
		txid := txn.ID()
		if tx.Bucket(bucketLimbo).Get(txid[:]) != nil {
			return nil  
		}
		return tx.Bucket(bucketLimbo).Put(txid[:], encoding.Marshal(LimboTransaction{
			Transaction: txn,
			LimboSince:  time.Now(),
		}))
	})
}
func (s *BoltDBStore) RemoveFromLimbo(id types.TransactionID) {
	s.update(func(tx *bolt.Tx) error {
		return tx.Bucket(bucketLimbo).Delete(id[:])
	})
}
func (s *BoltDBStore) LimboTransactions() (txns []LimboTransaction) {
	s.view(func(tx *bolt.Tx) error {
		c := tx.Bucket(bucketLimbo).Cursor()
		for k, v := c.Last(); k != nil; k, v = c.Prev() {
			var txn LimboTransaction
			encoding.Unmarshal(v, &txn)
			txns = append(txns, txn)
		}
		return nil
	})
	return
}
func (s *BoltDBStore) BlockRewards(n int) (brs []BlockReward) {
	s.view(func(tx *bolt.Tx) error {
		c := tx.Bucket(bucketBlockRewards).Cursor()
		for k, v := c.Last(); k != nil && len(brs) != n; k, v = c.Prev() {
			var br BlockReward
			encoding.Unmarshal(v, &br)
			brs = append(brs, br)
		}
		return nil
	})
	return
}
func (s *BoltDBStore) FileContracts(n int) (fcs []FileContract) {
	s.view(func(tx *bolt.Tx) error {
		c := tx.Bucket(bucketFileContracts).Cursor()
		for k, v := c.Last(); k != nil && len(fcs) != n; k, v = c.Prev() {
			var fc FileContract
			encoding.Unmarshal(v, &fc)
			fcs = append(fcs, fc)
		}
		return nil
	})
	return
}
func (s *BoltDBStore) FileContractHistory(id types.FileContractID) (history []FileContract) {
	s.view(func(tx *bolt.Tx) error {
		c := tx.Bucket(bucketFileContracts).Cursor()
		for k, v := c.Last(); k != nil; k, v = c.Prev() {
			var fc FileContract
			encoding.Unmarshal(v, &fc)
			if fc.ID == id {
				history = append(history, fc)
			}
		}
		return nil
	})
	return
}
func (s *BoltDBStore) Transactions(n int) (txids []types.TransactionID) {
	s.view(func(tx *bolt.Tx) error {
		c := tx.Bucket(bucketTxnsRecentIndex).Cursor()
		for k, v := c.Last(); k != nil && len(txids) != n; k, v = c.Prev() {
			var txid types.TransactionID
			copy(txid[:], v)
			txids = append(txids, txid)
		}
		return nil
	})
	return
}
func (s *BoltDBStore) TransactionsByAddress(addr types.UnlockHash, n int) (txids []types.TransactionID) {
	s.view(func(tx *bolt.Tx) error {
		addrTxnsBucket := tx.Bucket(bucketTxnsAddrIndex).Bucket(addr[:])
		if addrTxnsBucket == nil {
			return nil
		}
		c := addrTxnsBucket.Cursor()
		for k, v := c.Last(); k != nil && len(txids) != n; k, v = c.Prev() {
			var txid types.TransactionID
			copy(txid[:], v)
			txids = append(txids, txid)
		}
		return nil
	})
	return
}
func (s *BoltDBStore) Transaction(id types.TransactionID) (txn Transaction, exists bool) {
	s.view(func(tx *bolt.Tx) error {
		if v := tx.Bucket(bucketTxns).Get(id[:]); v != nil {
			encoding.Unmarshal(v, &txn)
			exists = true
		}
		return nil
	})
	return
}
func (s *BoltDBStore) SetMemo(txid types.TransactionID, memo []byte) {
	s.update(func(tx *bolt.Tx) error {
		tx.Bucket(bucketMemos).Put(txid[:], append([]byte(nil), memo...))
		return nil
	})
}
func (s *BoltDBStore) Memo(txid types.TransactionID) (memo []byte) {
	s.view(func(tx *bolt.Tx) error {
		memo = append([]byte(nil), tx.Bucket(bucketMemos).Get(txid[:])...)
		return nil
	})
	return
}
func (s *BoltDBStore) ChainHeight() (height types.BlockHeight) {
	s.view(func(tx *bolt.Tx) error {
		height = types.BlockHeight(binary.LittleEndian.Uint64(tx.Bucket(bucketMeta).Get(keyHeight)))
		if height > 0 {
			height--  
		}
		return nil
	})
	return
}
func (s *BoltDBStore) ConsensusChangeID() (ccid modules.ConsensusChangeID) {
	s.view(func(tx *bolt.Tx) error {
		copy(ccid[:], tx.Bucket(bucketMeta).Get(keyCCID))
		return nil
	})
	return
}
func (s *BoltDBStore) SeedIndex() (index uint64) {
	s.view(func(tx *bolt.Tx) error {
		index = binary.LittleEndian.Uint64(tx.Bucket(bucketMeta).Get(keySeedIndex))
		return nil
	})
	return
}
func (s *BoltDBStore) SetSeedIndex(index uint64) {
	s.update(func(tx *bolt.Tx) error {
		indexBytes := make([]byte, 8)
		binary.LittleEndian.PutUint64(indexBytes, index)
		tx.Bucket(bucketMeta).Put(keySeedIndex, indexBytes)
		return nil
	})
}
func (s *BoltDBStore) OwnsAddress(addr types.UnlockHash) (owned bool) {
	_, ok := s.addrs[addr]
	return ok
}
func (s *BoltDBStore) AddAddress(info SeedAddressInfo) {
	addr := CalculateUnlockHash(info.UnlockConditions)
	s.update(func(tx *bolt.Tx) error {
		if err := tx.Bucket(bucketAddrs).Put(addr[:], encoding.Marshal(info)); err != nil {
			return err
		}
		index := binary.LittleEndian.Uint64(tx.Bucket(bucketMeta).Get(keySeedIndex))
		if next := info.KeyIndex + 1; index < next {
			index = next
		}
		indexBytes := make([]byte, 8)
		binary.LittleEndian.PutUint64(indexBytes, index)
		return tx.Bucket(bucketMeta).Put(keySeedIndex, indexBytes)
	})
	s.addrs[addr] = struct{}{}
}
func (s *BoltDBStore) AddressInfo(addr types.UnlockHash) (info SeedAddressInfo, exists bool) {
	s.view(func(tx *bolt.Tx) error {
		if v := tx.Bucket(bucketAddrs).Get(addr[:]); v != nil {
			encoding.Unmarshal(v, &info)
			exists = true
		}
		return nil
	})
	return
}
func (s *BoltDBStore) RemoveAddress(addr types.UnlockHash) {
	s.update(func(tx *bolt.Tx) error {
		return tx.Bucket(bucketAddrs).Delete(addr[:])
	})
	delete(s.addrs, addr)
}