func (s *Store) Balance(ns walletdb.ReadBucket, minConf int32, syncOrder int32, coinId types.CoinID) (types.Amount, error) {
	bal, err := fetchMinedBalance(ns, coinId)
	if err != nil {
		return types.Amount{}, err
	}
	var op types.TxOutPoint
	var block Block
	err = ns.NestedReadBucket(bucketUnspent).ForEach(func(k, v []byte) error {
		err := readCanonicalOutPoint(k, &op)
		if err != nil {
			return err
		}
		err = readUnspentBlock(v, &block)
		if err != nil {
			return err
		}
		if existsRawUnMinedInput(ns, k) != nil {
			_, v := existsCredit(ns, &op.Hash, op.OutIndex, &block)
			amt, err := fetchRawCreditAmount(v)
			if err != nil {
				return err
			}
			bal.Value -= amt.Value
		}
		return nil
	})
	if err != nil {
		if _, ok := err.(Error); ok {
			return types.Amount{}, err
		}
		str := "failed iterating unspent outputs"
		return types.Amount{}, storeError(ErrDatabase, str, err)
	}
	coinBaseMaturity := int32(s.chainParams.CoinbaseMaturity)
	stopConf := minConf
	if coinBaseMaturity > stopConf {
		stopConf = coinBaseMaturity
	}
	lastOrder := syncOrder - stopConf
	blockIt := makeReadReverseBlockIterator(ns)
	for blockIt.prev() {
		block := &blockIt.elem
		if block.Order < lastOrder {
			break
		}
		for i := range block.transactions {
			txHash := &block.transactions[i]
			rec, err := fetchTxRecord(ns, txHash, &block.Block)
			if err != nil {
				return types.Amount{}, err
			}
			numOuts := uint32(len(rec.MsgTx.TxOut))
			for i := uint32(0); i < numOuts; i++ {
				opKey := canonicalOutPoint(txHash, i)
				if existsRawUnMinedInput(ns, opKey) != nil {
					continue
				}
				_, v := existsCredit(ns, txHash, i, &block.Block)
				if v == nil {
					continue
				}
				amt, spent, err := fetchRawCreditAmountSpent(v)
				if err != nil {
					return types.Amount{}, err
				}
				if spent {
					continue
				}
				confirms := syncOrder - block.Order + 1
				if confirms < minConf || (IsCoinBaseTx(&rec.MsgTx) &&
					confirms < coinBaseMaturity) {
					bal.Value -= amt.Value
				}
			}
		}
	}
	if blockIt.err != nil {
		return types.Amount{}, blockIt.err
	}
	if minConf == 0 {
		err = ns.NestedReadBucket(bucketUnMinedCredits).ForEach(func(k, v []byte) error {
			if existsRawUnMinedInput(ns, k) != nil {
				return nil
			}
			amount, err := fetchRawUnMinedCreditAmount(v)
			if err != nil {
				return err
			}
			bal.Value += amount.Value
			return nil
		})
		if err != nil {
			if _, ok := err.(Error); ok {
				return types.Amount{}, err
			}
			str := "failed to iterate over unMined credits bucket"
			return types.Amount{}, storeError(ErrDatabase, str, err)
		}
	}
	return bal, nil
}
Balance returns the spendable wallet balance (total value of all unspent
transaction outputs) given a minimum of minConf confirmations, calculated
at a current chain height of curHeight.  Coinbase outputs are only included
in the balance if maturity has been reached.

Balance may return unexpected results if syncHeight is lower than the block
height of the most recent mined transaction in the store.
package wtxmgr
import (
	"encoding/json"
	"fmt"
	util "github.com/Qitmeer/qitmeer-wallet/utils"
	"github.com/Qitmeer/qitmeer/common/math"
	corejson "github.com/Qitmeer/qitmeer/core/json"
	"time"
	"github.com/Qitmeer/qitmeer-wallet/walletdb"
	"github.com/Qitmeer/qitmeer/common/hash"
	"github.com/Qitmeer/qitmeer/core/types"
	"github.com/Qitmeer/qitmeer/log"
	"github.com/Qitmeer/qitmeer/params"
)
type Block struct {
	Hash  hash.Hash
	Order int32
}
type BlockMeta struct {
	Block
	Time time.Time
}
type blockRecord struct {
	Block
	Time         time.Time
	transactions []hash.Hash
}
type SpendTo struct {
	Index uint32
	TxId  hash.Hash
}
type TxInputPoint struct {
	TxOutPoint types.TxOutPoint
	SpendTo    SpendTo
}
type AddrTxOutput struct {
	Address  string
	TxId     hash.Hash
	Index    uint32
	Amount   types.Amount
	Block    Block
	Spend    SpendStatus
	SpendTo  *SpendTo
	Status   TxStatus
	Locked   uint32
	IsBlue   bool
	PkScript string
}
func NewAddrTxOutput() *AddrTxOutput {
	return &AddrTxOutput{SpendTo: &SpendTo{}}
}
func (a *AddrTxOutput) Encode() []byte {
	bytes, _ := util.Encode(a)
	return bytes
}
func DecodeAddrTxOutput(bytes []byte) (*AddrTxOutput, error) {
	output := &AddrTxOutput{}
	return output, util.Decode(bytes, output)
}
type AddrTxOutputs []AddrTxOutput
func (s AddrTxOutputs) Len() int { return len(s) }
func (s AddrTxOutputs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s AddrTxOutputs) Less(i, j int) bool {
	return s[i].Block.Order < s[j].Block.Order
}
type TxConfirmed struct {
	TxId          string
	Confirmations uint32
	TxStatus
}
type SpendStatus uint16
const (
	SpendStatusUnspent SpendStatus = 0
	SpendStatusSpend   SpendStatus = 1
)
type TxStatus uint16
const (
	TxStatusMemPool     TxStatus = 0
	TxStatusUnConfirmed TxStatus = 1
	TxStatusConfirmed   TxStatus = 2
	TxStatusFailed      TxStatus = 3
	TxStatusRead        TxStatus = 4
)
type UnconfirmTx struct {
	Order         uint32
	Confirmations uint32
}
func (u *UnconfirmTx) Marshal() []byte {
	bytes, _ := json.Marshal(u)
	return bytes
}
func UnMarshalUnconfirmTx(bytes []byte) (*UnconfirmTx, error) {
	u := &UnconfirmTx{}
	err := json.Unmarshal(bytes, u)
	return u, err
}
type UTxo struct {
	Address string
	TxId    string
	Index   uint32
	Amount  types.Amount
}
type incidence struct {
	txHash hash.Hash
	block  Block
}
type indexedIncidence struct {
	incidence
	index uint32
}
type credit struct {
	outPoint types.TxOutPoint
	block    Block
	amount   types.Amount
	change   bool
	spentBy  indexedIncidence  
}
type TxRecord struct {
	MsgTx        types.Transaction
	Hash         hash.Hash
	Received     time.Time
	SerializedTx []byte  
}
type Credit struct {
	types.TxOutPoint
	BlockMeta
	Amount       types.Amount
	PkScript     []byte
	Received     time.Time
	FromCoinBase bool
}
type Store struct {
	chainParams *params.Params
	NotifyUnspent func(hash *hash.Hash, index uint32)
}
func Open(ns walletdb.ReadBucket, chainParams *params.Params) (*Store, error) {
	err := openStore(ns)
	if err != nil {
		return nil, err
	}
	s := &Store{chainParams, nil}  
	return s, nil
}
func Create(ns walletdb.ReadWriteBucket) error {
	return createStore(ns)
}
func (s *Store) UpdateAddrTxIn(ns walletdb.ReadWriteBucket, addr string, outPoint *types.TxOutPoint) error {
	inRw, err := ns.CreateBucketIfNotExists([]byte(addr))
	if err != nil {
		return err
	} else {
		v := canonicalOutPoint(&outPoint.Hash, outPoint.OutIndex)
		err := inRw.Put(v, v)
		return err
	}
}
func (s *Store) InsertAddrTxOut(ns walletdb.ReadWriteBucket, txOut *AddrTxOutput) error {
	outRw, err := ns.CreateBucketIfNotExists([]byte(txOut.Address))
	if err != nil {
		return err
	} else {
		k := canonicalOutPoint(&txOut.TxId, txOut.Index)
		v := txOut.Encode()
		oldTxOut := outRw.Get(k)
		if oldTxOut == nil || len(oldTxOut) == 0 {
			err := outRw.Put(k, v)
			return err
		} else {
			addTxOutPut := NewAddrTxOutput()
			addTxOutPut, err := DecodeAddrTxOutput(oldTxOut)
			if err != nil {
				return err
			}
			if addTxOutPut.Spend != SpendStatusSpend {
				err := outRw.Put(k, v)
				return err
			} else {
				txOut.SpendTo = addTxOutPut.SpendTo
				txOut.Spend = addTxOutPut.Spend
				v := txOut.Encode()
				err := outRw.Put(k, v)
				return err
			}
		}
	}
}
func (s *Store) UpdateAddrTxOut(ns walletdb.ReadWriteBucket, txOut *AddrTxOutput) error {
	outRw, err := ns.CreateBucketIfNotExists([]byte(txOut.Address))
	if err != nil {
		return err
	} else {
		k := canonicalOutPoint(&txOut.TxId, txOut.Index)
		v := txOut.Encode()
		err := outRw.Put(k, v)
		return err
	}
}
func (s *Store) GetAddrTxOut(ns walletdb.ReadWriteBucket, address string, point types.TxOutPoint) (*AddrTxOutput, error) {
	outRw := ns.NestedReadWriteBucket([]byte(address))
	k := canonicalOutPoint(&point.Hash, point.OutIndex)
	txOut := outRw.Get(k)
	addTxOutPut := NewAddrTxOutput()
	addTxOutPut, err := DecodeAddrTxOutput(txOut)
	if err != nil {
		return nil, err
	}
	return addTxOutPut, nil
}
func (s *Store) updateMinedBalance(ns walletdb.ReadWriteBucket, rec *TxRecord,
	block *BlockMeta) error {
	minedBalance, err := fetchMinedBalance(ns, types.MEERID)
	if err != nil {
		return err
	}
	spender := indexedIncidence{
		incidence: incidence{
			txHash: rec.Hash,
			block:  block.Block,
		},
	}
	newMinedBalance := minedBalance
	for i, input := range rec.MsgTx.TxIn {
		unspentKey, credKey := existsUnspent(ns, &input.PreviousOut)
		if credKey == nil {
			continue
		}
		spender.index = uint32(i)
		amt, err := spendCredit(ns, credKey, &spender)
		if err != nil {
			return err
		}
		err = putDebit(
			ns, &rec.Hash, uint32(i), amt, &block.Block, credKey,
		)
		if err != nil {
			return err
		}
		if err := deleteRawUnspent(ns, unspentKey); err != nil {
			return err
		}
		newMinedBalance.Value -= amt.Value
	}
	cred := credit{
		outPoint: types.TxOutPoint{Hash: rec.Hash},
		block:    block.Block,
		spentBy:  indexedIncidence{index: ^uint32(0)},
	}
	it := makeUnMinedCreditIterator(ns, &rec.Hash)
	for it.next() {
		index, err := fetchRawUnMinedCreditIndex(it.ck)
		if err != nil {
			return err
		}
		amount, change, err := fetchRawUnMinedCreditAmountChange(it.cv)
		if err != nil {
			return err
		}
		cred.outPoint.OutIndex = index
		cred.amount = amount
		cred.change = change
		if err := putUnspentCredit(ns, &cred); err != nil {
			return err
		}
		err = putUnspent(ns, &cred.outPoint, &block.Block)
		if err != nil {
			return err
		}
		newMinedBalance.Value += amount.Value
	}
	if it.err != nil {
		return it.err
	}
	if newMinedBalance != minedBalance {
		return putMinedBalance(ns, newMinedBalance)
	}
	return nil
}
func (s *Store) deleteUnMinedTx(ns walletdb.ReadWriteBucket, rec *TxRecord) error {
	for i := range rec.MsgTx.TxOut {
		k := canonicalOutPoint(&rec.Hash, uint32(i))
		if err := deleteRawUnMinedCredit(ns, k); err != nil {
			return err
		}
	}
	return deleteRawUnmMined(ns, rec.Hash[:])
}
func (s *Store) InsertTx(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta) error {
	if block == nil {
		return s.insertMemPoolTx(ns, rec)
	}
	return s.insertMinedTx(ns, rec, block)
}
func (s *Store) RemoveUnMinedTx(ns walletdb.ReadWriteBucket, rec *TxRecord) error {
	return s.removeConflict(ns, rec)
}
func (s *Store) insertMinedTx(ns walletdb.ReadWriteBucket, rec *TxRecord,
	block *BlockMeta) error {
	if _, v := existsTxRecord(ns, &rec.Hash, &block.Block); v != nil {
		return nil
	}
	var err error
	blockKey, blockValue := existsBlockRecord(ns, uint32(block.Order))
	if blockValue == nil {
		err = putBlockRecord(ns, block, &rec.Hash)
	} else {
		blockValue, err = appendRawBlockRecord(blockValue, &rec.Hash)
		if err != nil {
			return err
		}
		err = putRawBlockRecord(ns, blockKey, blockValue)
	}
	if err != nil {
		return err
	}
	if err := putTxRecord(ns, rec, &block.Block); err != nil {
		return err
	}
	if err := s.updateMinedBalance(ns, rec, block); err != nil {
		return err
	}
	if v := existsRawUnMined(ns, rec.Hash[:]); v != nil {
		log.Info(fmt.Sprintf("Marking unconfirmed transaction %v mined in block %d",
			&rec.Hash, block.Order))
		if err := s.deleteUnMinedTx(ns, rec); err != nil {
			return err
		}
	}
	return s.removeDoubleSpends(ns, rec)
}
func (s *Store) AddCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, index uint32, change bool) error {
	if int(index) >= len(rec.MsgTx.TxOut) {
		str := "transaction output does not exist"
		return storeError(ErrInput, str, nil)
	}
	isNew, err := s.addCredit(ns, rec, block, index, change)
	if err == nil && isNew && s.NotifyUnspent != nil {
		s.NotifyUnspent(&rec.Hash, index)
	}
	return err
}
func (s *Store) addCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, index uint32, change bool) (bool, error) {
	if block == nil {
		k := canonicalOutPoint(&rec.Hash, index)
		if existsRawUnMinedCredit(ns, k) != nil {
			return false, nil
		}
		if existsRawUnspent(ns, k) != nil {
			return false, nil
		}
		v := valueUnMinedCredit(rec.MsgTx.TxOut[index].Amount, change)
		return true, putRawUnMinedCredit(ns, k, v)
	}
	k, v := existsCredit(ns, &rec.Hash, index, &block.Block)
	if v != nil {
		return false, nil
	}
	txOutAmt := rec.MsgTx.TxOut[index].Amount
	log.Debug("Marking transaction %v output %d (%v) spendable",
		rec.Hash, index, txOutAmt)
	cred := credit{
		outPoint: types.TxOutPoint{
			Hash:     rec.Hash,
			OutIndex: index,
		},
		block:   block.Block,
		amount:  txOutAmt,
		change:  change,
		spentBy: indexedIncidence{index: ^uint32(0)},
	}
	v = valueUnspentCredit(&cred)
	err := putRawCredit(ns, k, v)
	if err != nil {
		return false, err
	}
	minedBalance, err := fetchMinedBalance(ns, txOutAmt.Id)
	if err != nil {
		return false, err
	}
	a := types.Amount{Value: minedBalance.Value + txOutAmt.Value, Id: txOutAmt.Id}
	err = putMinedBalance(ns, a)
	if err != nil {
		return false, err
	}
	return true, putUnspent(ns, &cred.outPoint, &block.Block)
}
func (s *Store) Rollback(ns walletdb.ReadWriteBucket, height int32) error {
	return s.rollback(ns, height)
}
var (
	zeroHash = &hash.ZeroHash
)
func IsCoinBaseTx(tx *types.Transaction) bool {
	if len(tx.TxIn) != 1 {
		return false
	}
	prevOut := &tx.TxIn[0].PreviousOut
	if prevOut.OutIndex != math.MaxUint32 || !prevOut.Hash.IsEqual(zeroHash) {
		return false
	}
	return true
}
func TxRawIsCoinBase(tx corejson.TxRawResult) bool {
	for _, vin := range tx.Vin {
		if vin.Coinbase != "" {
			return true
		}
	}
	return false
}
func (s *Store) rollback(ns walletdb.ReadWriteBucket, height int32) error {
	minedBalance, err := fetchMinedBalance(ns, types.MEERID)
	if err != nil {
		return err
	}
	var coinBaseCredits []types.TxOutPoint
	var heightsToRemove []int32
	it := makeReverseBlockIterator(ns)
	for it.prev() {
		b := &it.elem
		if it.elem.Order < height {
			break
		}
		heightsToRemove = append(heightsToRemove, it.elem.Order)
		log.Info("Rolling back transactions", "transactions", len(b.transactions), "block", b.Hash, "height", b.Order)
		for i := range b.transactions {
			txHash := &b.transactions[i]
			recKey := keyTxRecord(txHash, &b.Block)
			recVal := existsRawTxRecord(ns, recKey)
			var rec TxRecord
			err = readRawTxRecord(txHash, recVal, &rec)
			if err != nil {
				return err
			}
			err = deleteTxRecord(ns, txHash, &b.Block)
			if err != nil {
				return err
			}
			if IsCoinBaseTx(&rec.MsgTx) {
				op := types.TxOutPoint{Hash: rec.Hash}
				for i, output := range rec.MsgTx.TxOut {
					k, v := existsCredit(ns, &rec.Hash,
						uint32(i), &b.Block)
					if v == nil {
						continue
					}
					op.OutIndex = uint32(i)
					coinBaseCredits = append(coinBaseCredits, op)
					unspentKey, credKey := existsUnspent(ns, &op)
					if credKey != nil {
						minedBalance.Value -= output.Amount.Value
						err = deleteRawUnspent(ns, unspentKey)
						if err != nil {
							return err
						}
					}
					err = deleteRawCredit(ns, k)
					if err != nil {
						return err
					}
				}
				continue
			}
			err = putRawUnMined(ns, txHash[:], recVal)
			if err != nil {
				return err
			}
			for i, input := range rec.MsgTx.TxIn {
				prevOut := &input.PreviousOut
				prevOutKey := canonicalOutPoint(&prevOut.Hash,
					prevOut.OutIndex)
				err = putRawUnMinedInput(ns, prevOutKey, rec.Hash[:])
				if err != nil {
					return err
				}
				debKey, credKey, err := existsDebit(ns,
					&rec.Hash, uint32(i), &b.Block)
				if err != nil {
					return err
				}
				if debKey == nil {
					continue
				}
				var amt types.Amount
				amt, err = unspendRawCredit(ns, credKey)
				if err != nil {
					return err
				}
				err = deleteRawDebit(ns, debKey)
				if err != nil {
					return err
				}
				if amt.Value == 0 {
					continue
				}
				unspentVal, err := fetchRawCreditUnspentValue(credKey)
				if err != nil {
					return err
				}
				minedBalance.Value += amt.Value
				err = putRawUnspent(ns, prevOutKey, unspentVal)
				if err != nil {
					return err
				}
			}
			for i, output := range rec.MsgTx.TxOut {
				k, v := existsCredit(ns, &rec.Hash, uint32(i),
					&b.Block)
				if v == nil {
					continue
				}
				amt, change, err := fetchRawCreditAmountChange(v)
				if err != nil {
					return err
				}
				outPointKey := canonicalOutPoint(&rec.Hash, uint32(i))
				unMinedCredVal := valueUnMinedCredit(amt, change)
				err = putRawUnMinedCredit(ns, outPointKey, unMinedCredVal)
				if err != nil {
					return err
				}
				err = deleteRawCredit(ns, k)
				if err != nil {
					return err
				}
				credKey := existsRawUnspent(ns, outPointKey)
				if credKey != nil {
					minedBalance.Value -= output.Amount.Value
					err = deleteRawUnspent(ns, outPointKey)
					if err != nil {
						return err
					}
				}
			}
		}
		it.reposition(uint32(it.elem.Order))
	}
	if it.err != nil {
		return it.err
	}
	for _, h := range heightsToRemove {
		err = deleteBlockRecord(ns, uint32(h))
		if err != nil {
			return err
		}
	}
	for _, op := range coinBaseCredits {
		opKey := canonicalOutPoint(&op.Hash, op.OutIndex)
		unMinedSpendTxHashKeys := fetchUnMinedInputSpendTxHashes(ns, opKey)
		for _, unMinedSpendTxHashKey := range unMinedSpendTxHashKeys {
			unMinedVal := existsRawUnMined(ns, unMinedSpendTxHashKey[:])
			if unMinedVal == nil {
				continue
			}
			var unMinedRec TxRecord
			unMinedRec.Hash = unMinedSpendTxHashKey
			err = readRawTxRecord(&unMinedRec.Hash, unMinedVal, &unMinedRec)
			if err != nil {
				return err
			}
			log.Debug("Transaction %v spends a removed coinbase "+
				"output -- removing as well", unMinedRec.Hash)
			err = s.removeConflict(ns, &unMinedRec)
			if err != nil {
				return err
			}
		}
	}
	return putMinedBalance(ns, minedBalance)
}
func (s *Store) UnspentOutputs(ns walletdb.ReadBucket) ([]Credit, error) {
	var unspent []Credit
	var op types.TxOutPoint
	var block Block
	err := ns.NestedReadBucket(bucketUnspent).ForEach(func(k, v []byte) error {
		err := readCanonicalOutPoint(k, &op)
		if err != nil {
			return err
		}
		if existsRawUnMinedInput(ns, k) != nil {
			return nil
		}
		err = readUnspentBlock(v, &block)
		if err != nil {
			return err
		}
		blockTime, err := fetchBlockTime(ns, uint32(block.Order))
		if err != nil {
			return err
		}
		rec, err := fetchTxRecord(ns, &op.Hash, &block)
		if err != nil {
			return err
		}
		txOut := rec.MsgTx.TxOut[op.OutIndex]
		cred := Credit{
			TxOutPoint: op,
			BlockMeta: BlockMeta{
				Block: block,
				Time:  blockTime,
			},
			Amount:       types.Amount(txOut.Amount),
			PkScript:     txOut.PkScript,
			Received:     rec.Received,
			FromCoinBase: IsCoinBaseTx(&rec.MsgTx),
		}
		unspent = append(unspent, cred)
		return nil
	})
	if err != nil {
		if _, ok := err.(Error); ok {
			return nil, err
		}
		str := "failed iterating unspent bucket"
		return nil, storeError(ErrDatabase, str, err)
	}
	err = ns.NestedReadBucket(bucketUnMinedCredits).ForEach(func(k, v []byte) error {
		if existsRawUnMinedInput(ns, k) != nil {
			return nil
		}
		err := readCanonicalOutPoint(k, &op)
		if err != nil {
			return err
		}
		recVal := existsRawUnMined(ns, op.Hash[:])
		var rec TxRecord
		err = readRawTxRecord(&op.Hash, recVal, &rec)
		if err != nil {
			return err
		}
		txOut := rec.MsgTx.TxOut[op.OutIndex]
		cred := Credit{
			TxOutPoint: op,
			BlockMeta: BlockMeta{
				Block: Block{Order: -1},
			},
			Amount:       types.Amount(txOut.Amount),
			PkScript:     txOut.PkScript,
			Received:     rec.Received,
			FromCoinBase: IsCoinBaseTx(&rec.MsgTx),
		}
		unspent = append(unspent, cred)
		return nil
	})
	if err != nil {
		if _, ok := err.(Error); ok {
			return nil, err
		}
		str := "failed iterating unMined credits bucket"
		return nil, storeError(ErrDatabase, str, err)
	}
	return unspent, nil
}