func Rescan() {
	globalData.Lock()
	defer globalData.Unlock()
	globalData.spend = make(map[spendKey]uint64)
	for _, item := range globalData.pendingTransactions {
		rescanItem(item.tx)
	}
	for _, item := range globalData.pendingFreeIssues {
		for _, tx := range item.txs {
			rescanItem(tx)
		}
	}
	for _, item := range globalData.pendingPaidIssues {
		for _, tx := range item.txs {
			rescanItem(tx)
		}
	}
	for _, tx := range globalData.verifiedTransactions {
		rescanItem(tx)
	}
	for _, item := range globalData.verifiedFreeIssues {
		for _, tx := range item.txs {
			rescanItem(tx)
		}
	}
	for _, item := range globalData.verifiedPaidIssues {
		for _, tx := range item.txs {
			rescanItem(tx)
		}
	}
}
Rescan - before calling Enable may need to run rescan to drop any
invalidated transactions especially if the block height has changed
package reservoir
import (
	"path"
	"sync"
	"time"
	"github.com/bitmark-inc/bitmarkd/account"
	"github.com/bitmark-inc/bitmarkd/background"
	"github.com/bitmark-inc/bitmarkd/blockrecord"
	"github.com/bitmark-inc/bitmarkd/currency"
	"github.com/bitmark-inc/bitmarkd/difficulty"
	"github.com/bitmark-inc/bitmarkd/fault"
	"github.com/bitmark-inc/bitmarkd/merkle"
	"github.com/bitmark-inc/bitmarkd/ownership"
	"github.com/bitmark-inc/bitmarkd/pay"
	"github.com/bitmark-inc/bitmarkd/storage"
	"github.com/bitmark-inc/bitmarkd/transactionrecord"
	"github.com/bitmark-inc/logger"
)
const (
	MaximumIssues = 100  
)
const (
	maximumPendingFreeIssues   = blockrecord.MaximumTransactions * 2
	maximumPendingPaidIssues   = blockrecord.MaximumTransactions * 2
	maximumPendingTransactions = blockrecord.MaximumTransactions * 16
)
const reservoirFile = "reservoir.cache"
type transactionData struct {
	txId        merkle.Digest                  
	transaction transactionrecord.Transaction  
	packed      transactionrecord.Packed       
}
type transactionPaymentData struct {
	tx        *transactionData                        
	payId     pay.PayId                               
	payments  []transactionrecord.PaymentAlternative  
	expiresAt time.Time                               
}
type issuePaymentData struct {
	txs       []*transactionData                      
	payId     pay.PayId                               
	payments  []transactionrecord.PaymentAlternative  
	expiresAt time.Time                               
}
type issueFreeData struct {
	txs        []*transactionData      
	payId      pay.PayId               
	nonce      PayNonce                
	difficulty *difficulty.Difficulty  
	expiresAt  time.Time               
}
type PaymentDetail struct {
	Currency currency.Currency  
	TxID     string             
	Amounts  map[string]uint64  
}
type spendKey struct {
	owner [64]byte
	share merkle.Digest
}
type Handles struct {
	Assets            storage.Handle
	BlockOwnerPayment storage.Handle
	Blocks            storage.Handle
	Transactions      storage.Handle
	OwnerTxIndex      storage.Handle
	OwnerData         storage.Handle
	Shares            storage.Handle
	ShareQuantity     storage.Handle
}
type globalDataType struct {
	sync.RWMutex
	enabled bool
	filename string
	log *logger.L
	background *background.T
	verifiedTransactions map[pay.PayId]*transactionData   
	verifiedFreeIssues   map[pay.PayId]*issueFreeData     
	verifiedPaidIssues   map[pay.PayId]*issuePaymentData  
	verifiedIndex        map[merkle.Digest]pay.PayId      
	inProgressLinks map[merkle.Digest]merkle.Digest
	pendingTransactions map[pay.PayId]*transactionPaymentData
	pendingFreeIssues   map[pay.PayId]*issueFreeData
	pendingPaidIssues   map[pay.PayId]*issuePaymentData
	pendingIndex        map[merkle.Digest]pay.PayId  
	pendingFreeCount int
	pendingPaidCount int
	orphanPayments map[pay.PayId]*PaymentDetail
	spend map[spendKey]uint64
	initialised bool
	handles Handles
}
var globalData globalDataType
func (g *globalDataType) StoreTransfer(transfer transactionrecord.BitmarkTransfer) (*TransferInfo, bool, error) {
	return storeTransfer(
		transfer,
		g.handles.Transactions,
		g.handles.OwnerTxIndex,
		g.handles.OwnerData,
		g.handles.BlockOwnerPayment,
	)
}
func (g *globalDataType) StoreIssues(issues []*transactionrecord.BitmarkIssue) (*IssueInfo, bool, error) {
	return storeIssues(
		issues,
		g.handles.Assets,
		g.handles.BlockOwnerPayment,
	)
}
func (g *globalDataType) TryProof(payID pay.PayId, clientNonce []byte) TrackingStatus {
	return tryProof(payID, clientNonce)
}
func (g *globalDataType) TransactionStatus(txID merkle.Digest) TransactionState {
	return transactionStatus(txID)
}
func (g *globalDataType) ShareBalance(owner *account.Account, startSharedID merkle.Digest, count int) ([]BalanceInfo, error) {
	return shareBalance(owner, startSharedID, count, g.handles.ShareQuantity)
}
func (g *globalDataType) StoreGrant(grant *transactionrecord.ShareGrant) (*GrantInfo, bool, error) {
	return storeGrant(
		grant,
		g.handles.ShareQuantity,
		g.handles.Shares,
		g.handles.OwnerData,
		g.handles.BlockOwnerPayment,
		g.handles.Transactions,
	)
}
func (g *globalDataType) StoreSwap(swap *transactionrecord.ShareSwap) (*SwapInfo, bool, error) {
	return storeSwap(
		swap,
		g.handles.ShareQuantity,
		g.handles.Shares,
		g.handles.OwnerData,
		g.handles.BlockOwnerPayment,
	)
}
type Reservoir interface {
	StoreTransfer(transactionrecord.BitmarkTransfer) (*TransferInfo, bool, error)
	StoreIssues(issues []*transactionrecord.BitmarkIssue) (*IssueInfo, bool, error)
	TryProof(pay.PayId, []byte) TrackingStatus
	TransactionStatus(merkle.Digest) TransactionState
	ShareBalance(*account.Account, merkle.Digest, int) ([]BalanceInfo, error)
	StoreGrant(*transactionrecord.ShareGrant) (*GrantInfo, bool, error)
	StoreSwap(swap *transactionrecord.ShareSwap) (*SwapInfo, bool, error)
}
func Get() Reservoir {
	if globalData.initialised {
		return &globalData
	}
	return nil
}
func Initialise(cacheDirectory string, handles Handles) error {
	globalData.Lock()
	defer globalData.Unlock()
	if globalData.initialised {
		return fault.AlreadyInitialised
	}
	globalData.log = logger.New("reservoir")
	globalData.log.Info("starting…")
	globalData.inProgressLinks = make(map[merkle.Digest]merkle.Digest)
	globalData.verifiedTransactions = make(map[pay.PayId]*transactionData)
	globalData.verifiedFreeIssues = make(map[pay.PayId]*issueFreeData)
	globalData.verifiedPaidIssues = make(map[pay.PayId]*issuePaymentData)
	globalData.verifiedIndex = make(map[merkle.Digest]pay.PayId)
	globalData.pendingTransactions = make(map[pay.PayId]*transactionPaymentData)
	globalData.pendingFreeIssues = make(map[pay.PayId]*issueFreeData)
	globalData.pendingPaidIssues = make(map[pay.PayId]*issuePaymentData)
	globalData.pendingIndex = make(map[merkle.Digest]pay.PayId)
	globalData.pendingFreeCount = 0
	globalData.pendingPaidCount = 0
	globalData.orphanPayments = make(map[pay.PayId]*PaymentDetail)
	globalData.spend = make(map[spendKey]uint64)
	globalData.enabled = true
	globalData.filename = path.Join(cacheDirectory, reservoirFile)
	globalData.handles = handles
	globalData.initialised = true
	globalData.log.Debugf("load from file: %s", globalData.filename)
	globalData.log.Info("start background…")
	processes := background.Processes{
		&rebroadcaster{},
		&cleaner{},
	}
	globalData.background = background.Start(processes, nil)
	return nil
}
func Finalise() error {
	if !globalData.initialised {
		return fault.NotInitialised
	}
	globalData.log.Info("shutting down…")
	globalData.log.Flush()
	globalData.background.Stop()
	saveToFile()
	globalData.initialised = false
	globalData.log.Info("finished")
	globalData.log.Flush()
	return nil
}
func ReadCounters() (int, int) {
	globalData.RLock()
	pending := len(globalData.pendingIndex)
	verified := len(globalData.verifiedIndex)
	globalData.RUnlock()
	return pending, verified
}
type TransactionState int
const (
	StateUnknown   TransactionState = iota
	StatePending   TransactionState = iota
	StateVerified  TransactionState = iota
	StateConfirmed TransactionState = iota
)
func (state TransactionState) String() string {
	switch state {
	case StateUnknown:
		return "Unknown"
	case StatePending:
		return "Pending"
	case StateVerified:
		return "Verified"
	case StateConfirmed:
		return "Confirmed"
	default:
		return "Unknown"
	}
}
func transactionStatus(txId merkle.Digest) TransactionState {
	globalData.RLock()
	defer globalData.RUnlock()
	_, ok := globalData.pendingIndex[txId]
	if ok {
		return StatePending
	}
	_, ok = globalData.verifiedIndex[txId]
	if ok {
		return StateVerified
	}
	if storage.Pool.Transactions.Has(txId[:]) {
		return StateConfirmed
	}
	return StateUnknown
}
func setVerified(payId pay.PayId, detail *PaymentDetail) bool {
	if nil == detail {
		globalData.log.Warn("payment was not provided")
		return false
	}
	globalData.log.Infof("detail: currency: %s, amounts: %#v", detail.Currency, detail.Amounts)
	if entry, ok := globalData.pendingTransactions[payId]; ok {
		if !acceptablePayment(detail, entry.payments) {
			globalData.log.Warnf("single transaction failed check for txid: %s  payid: %s", detail.TxID, payId)
			return false
		}
		globalData.log.Infof("paid txid: %s  payid: %s", detail.TxID, payId)
		delete(globalData.pendingTransactions, payId)
		globalData.verifiedTransactions[payId] = entry.tx
		txId := entry.tx.txId
		delete(globalData.pendingIndex, txId)
		globalData.verifiedIndex[txId] = payId
		return true
	}
	if entry, ok := globalData.pendingPaidIssues[payId]; ok {
		if !acceptablePayment(detail, entry.payments) {
			globalData.log.Warnf("issue block failed check for txid: %s  payid: %s", detail.TxID, payId)
			return false
		}
		globalData.log.Infof("paid txid: %s  payid: %s", detail.TxID, payId)
		globalData.pendingPaidCount -= len(entry.txs)
		delete(globalData.pendingPaidIssues, payId)
		globalData.verifiedPaidIssues[payId] = entry
		for _, tx := range entry.txs {
			txId := tx.txId
			delete(globalData.pendingIndex, txId)
			globalData.verifiedIndex[txId] = payId
		}
		return true
	}
	return false
}
func acceptablePayment(detail *PaymentDetail, payments []transactionrecord.PaymentAlternative) bool {
next_currency:
	for _, p := range payments {
		acceptable := true
		for _, item := range p {
			if item.Currency != detail.Currency {
				continue next_currency
			}
			if detail.Amounts[item.Address] < item.Amount {
				acceptable = false
			}
		}
		if acceptable {
			return true
		}
	}
	return false
}
func SetTransferVerified(payId pay.PayId, detail *PaymentDetail) {
	globalData.log.Infof("txid: %s  payid: %s", detail.TxID, payId)
	globalData.Lock()
	if !setVerified(payId, detail) {
		globalData.log.Debugf("orphan payment: txid: %s  payid: %s", detail.TxID, payId)
		globalData.orphanPayments[payId] = detail
	}
	globalData.Unlock()
}
func Disable() {
	globalData.Lock()
	globalData.enabled = false
	globalData.Unlock()
}
func Enable() {
	globalData.Lock()
	globalData.enabled = true
	globalData.Unlock()
}
func ClearSpend() {
	globalData.Lock()
	defer globalData.Unlock()
	if globalData.enabled {
		logger.Panic("reservoir clear spend when not locked")
	}
	globalData.spend = make(map[spendKey]uint64)
}