func dbPutUtxoView(dbTx database.Tx, view *UtxoViewpoint) error {
	utxoBucket := dbTx.Metadata().Bucket(utxoSetBucketName)
	for txHashIter, entry := range view.entries {
		if entry == nil || !entry.modified {
			continue
		}
		serialized, err := serializeUtxoEntry(entry)
		if err != nil {
			return err
		}
		txHash := txHashIter
		if serialized == nil {
			if err := utxoBucket.Delete(txHash[:]); err != nil {
				return err
			}
			continue
		}
		err = utxoBucket.Put(txHash[:], serialized)
		if err != nil {
			return err
		}
	}
	return nil
}
dbPutUtxoView uses an existing database transaction to update the utxo set
in the database based on the provided utxo view contents and state.  In
particular, only the entries that have been marked as modified are written
to the database.
package blockchain
import (
	"bytes"
	"encoding/binary"
	"fmt"
	"github.com/bitgo/prova/btcec"
	"github.com/bitgo/prova/chaincfg/chainhash"
	"github.com/bitgo/prova/database"
	"github.com/bitgo/prova/provautil"
	"github.com/bitgo/prova/wire"
	"math/big"
	"sort"
	"time"
)
var (
	hashIndexBucketName = []byte("hashidx")
	heightIndexBucketName = []byte("heightidx")
	chainStateKeyName = []byte("chainstate")
	spendJournalBucketName = []byte("spendjournal")
	utxoSetBucketName = []byte("utxoset")
	keySetBucketName = []byte("keyset")
	byteOrder = binary.LittleEndian
)
type errNotInMainChain string
func (e errNotInMainChain) Error() string {
	return string(e)
}
func isNotInMainChainErr(err error) bool {
	_, ok := err.(errNotInMainChain)
	return ok
}
type errDeserialize string
func (e errDeserialize) Error() string {
	return string(e)
}
func isDeserializeErr(err error) bool {
	_, ok := err.(errDeserialize)
	return ok
}
type spentTxOut struct {
	compressed bool    
	version    int32   
	amount     int64   
	pkScript   []byte  
	height     uint32  
	isCoinBase bool    
}
func spentTxOutHeaderCode(stxo *spentTxOut) uint64 {
	if stxo.height == 0 {
		return 0
	}
	headerCode := uint64(stxo.height) << 1
	if stxo.isCoinBase {
		headerCode |= 0x01
	}
	return headerCode
}
func spentTxOutSerializeSize(stxo *spentTxOut) int {
	headerCode := spentTxOutHeaderCode(stxo)
	size := serializeSizeVLQ(headerCode)
	if headerCode != 0 {
		size += serializeSizeVLQ(uint64(stxo.version))
	}
	return size + compressedTxOutSize(uint64(stxo.amount), stxo.pkScript,
		stxo.version, stxo.compressed)
}
func putSpentTxOut(target []byte, stxo *spentTxOut) int {
	headerCode := spentTxOutHeaderCode(stxo)
	offset := putVLQ(target, headerCode)
	if headerCode != 0 {
		offset += putVLQ(target[offset:], uint64(stxo.version))
	}
	return offset + putCompressedTxOut(target[offset:], uint64(stxo.amount),
		stxo.pkScript, stxo.version, stxo.compressed)
}
func decodeSpentTxOut(serialized []byte, stxo *spentTxOut, txVersion int32) (int, error) {
	if len(serialized) == 0 {
		return 0, errDeserialize("no serialized bytes")
	}
	code, offset := deserializeVLQ(serialized)
	if offset >= len(serialized) {
		return offset, errDeserialize("unexpected end of data after " +
			"header code")
	}
	if code != 0 {
		version, bytesRead := deserializeVLQ(serialized[offset:])
		offset += bytesRead
		if offset >= len(serialized) {
			return offset, errDeserialize("unexpected end of data " +
				"after version")
		}
		stxo.isCoinBase = code&0x01 != 0
		stxo.height = uint32(code >> 1)
		stxo.version = int32(version)
	} else {
		if txVersion == -1 {
			return offset, AssertError("decodeSpentTxOut called " +
				"without a containing tx version when the " +
				"serialized stxo that does not encode the " +
				"version")
		}
		stxo.version = txVersion
	}
	compAmount, compScript, bytesRead, err := decodeCompressedTxOut(
		serialized[offset:], stxo.version)
	offset += bytesRead
	if err != nil {
		return offset, errDeserialize(fmt.Sprintf("unable to decode "+
			"txout: %v", err))
	}
	stxo.amount = int64(compAmount)
	stxo.pkScript = compScript
	stxo.compressed = true
	return offset, nil
}
func deserializeSpendJournalEntry(serialized []byte, txns []*wire.MsgTx, view *UtxoViewpoint) ([]spentTxOut, error) {
	var numStxos int
	for _, tx := range txns {
		numStxos += len(tx.TxIn)
	}
	if len(serialized) == 0 {
		if numStxos != 0 {
			return nil, AssertError(fmt.Sprintf("mismatched spend "+
				"journal serialization - no serialization for "+
				"expected %d stxos", numStxos))
		}
		return nil, nil
	}
	stxoIdx := numStxos - 1
	stxoInFlight := make(map[chainhash.Hash]int)
	offset := 0
	stxos := make([]spentTxOut, numStxos)
	for txIdx := len(txns) - 1; txIdx > -1; txIdx-- {
		tx := txns[txIdx]
		for txInIdx := len(tx.TxIn) - 1; txInIdx > -1; txInIdx-- {
			txIn := tx.TxIn[txInIdx]
			stxo := &stxos[stxoIdx]
			stxoIdx--
			txVersion := int32(-1)
			originHash := &txIn.PreviousOutPoint.Hash
			entry := view.LookupEntry(originHash)
			if entry != nil {
				txVersion = entry.Version()
			} else if idx, ok := stxoInFlight[*originHash]; ok {
				txVersion = stxos[idx].version
			} else {
				stxoInFlight[*originHash] = stxoIdx + 1
			}
			n, err := decodeSpentTxOut(serialized[offset:], stxo,
				txVersion)
			offset += n
			if err != nil {
				return nil, errDeserialize(fmt.Sprintf("unable "+
					"to decode stxo for %v: %v",
					txIn.PreviousOutPoint, err))
			}
		}
	}
	return stxos, nil
}
func serializeSpendJournalEntry(stxos []spentTxOut) []byte {
	if len(stxos) == 0 {
		return nil
	}
	var size int
	for i := range stxos {
		size += spentTxOutSerializeSize(&stxos[i])
	}
	serialized := make([]byte, size)
	var offset int
	for i := len(stxos) - 1; i > -1; i-- {
		offset += putSpentTxOut(serialized[offset:], &stxos[i])
	}
	return serialized
}
func dbFetchSpendJournalEntry(dbTx database.Tx, block *provautil.Block, view *UtxoViewpoint) ([]spentTxOut, error) {
	spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName)
	serialized := spendBucket.Get(block.Hash()[:])
	blockTxns := block.MsgBlock().Transactions[1:]
	stxos, err := deserializeSpendJournalEntry(serialized, blockTxns, view)
	if err != nil {
		if isDeserializeErr(err) {
			return nil, database.Error{
				ErrorCode: database.ErrCorruption,
				Description: fmt.Sprintf("corrupt spend "+
					"information for %v: %v", block.Hash(),
					err),
			}
		}
		return nil, err
	}
	return stxos, nil
}
func dbPutSpendJournalEntry(dbTx database.Tx, blockHash *chainhash.Hash, stxos []spentTxOut) error {
	spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName)
	serialized := serializeSpendJournalEntry(stxos)
	return spendBucket.Put(blockHash[:], serialized)
}
func dbRemoveSpendJournalEntry(dbTx database.Tx, blockHash *chainhash.Hash) error {
	spendBucket := dbTx.Metadata().Bucket(spendJournalBucketName)
	return spendBucket.Delete(blockHash[:])
}
func utxoEntryHeaderCode(entry *UtxoEntry, highestOutputIndex uint32) (uint64, int, error) {
	numBitmapBytes := int((highestOutputIndex + 6) / 8)
	output0Unspent := !entry.IsOutputSpent(0)
	output1Unspent := !entry.IsOutputSpent(1)
	var numBitmapBytesAdjustment int
	if !output0Unspent && !output1Unspent {
		if numBitmapBytes == 0 {
			return 0, 0, AssertError("attempt to serialize utxo " +
				"header for fully spent transaction")
		}
		numBitmapBytesAdjustment = 1
	}
	headerCode := uint64(numBitmapBytes-numBitmapBytesAdjustment) << 3
	if entry.isCoinBase {
		headerCode |= 0x01  
	}
	if output0Unspent {
		headerCode |= 0x02  
	}
	if output1Unspent {
		headerCode |= 0x04  
	}
	return headerCode, numBitmapBytes, nil
}
func serializeUtxoEntry(entry *UtxoEntry) ([]byte, error) {
	if entry.IsFullySpent() {
		return nil, nil
	}
	outputOrder := make([]int, 0, len(entry.sparseOutputs))
	for outputIndex := range entry.sparseOutputs {
		outputOrder = append(outputOrder, int(outputIndex))
	}
	sort.Ints(outputOrder)
	highIndex := uint32(outputOrder[len(outputOrder)-1])
	headerCode, numBitmapBytes, err := utxoEntryHeaderCode(entry, highIndex)
	if err != nil {
		return nil, err
	}
	size := serializeSizeVLQ(uint64(entry.version)) +
		serializeSizeVLQ(uint64(entry.blockHeight)) +
		serializeSizeVLQ(headerCode) + numBitmapBytes
	for _, outputIndex := range outputOrder {
		out := entry.sparseOutputs[uint32(outputIndex)]
		if out.spent {
			continue
		}
		size += compressedTxOutSize(uint64(out.amount), out.pkScript,
			entry.version, out.compressed)
	}
	serialized := make([]byte, size)
	offset := putVLQ(serialized, uint64(entry.version))
	offset += putVLQ(serialized[offset:], uint64(entry.blockHeight))
	offset += putVLQ(serialized[offset:], headerCode)
	for i := uint32(0); i < uint32(numBitmapBytes); i++ {
		unspentBits := byte(0)
		for j := uint32(0); j < 8; j++ {
			if !entry.IsOutputSpent(2 + i*8 + j) {
				unspentBits |= 1 << uint8(j)
			}
		}
		serialized[offset] = unspentBits
		offset++
	}
	for _, outputIndex := range outputOrder {
		out := entry.sparseOutputs[uint32(outputIndex)]
		if out.spent {
			continue
		}
		offset += putCompressedTxOut(serialized[offset:],
			uint64(out.amount), out.pkScript, entry.version,
			out.compressed)
	}
	return serialized, nil
}
func deserializeUtxoEntry(serialized []byte) (*UtxoEntry, error) {
	version, bytesRead := deserializeVLQ(serialized)
	offset := bytesRead
	if offset >= len(serialized) {
		return nil, errDeserialize("unexpected end of data after version")
	}
	blockHeight, bytesRead := deserializeVLQ(serialized[offset:])
	offset += bytesRead
	if offset >= len(serialized) {
		return nil, errDeserialize("unexpected end of data after height")
	}
	code, bytesRead := deserializeVLQ(serialized[offset:])
	offset += bytesRead
	if offset >= len(serialized) {
		return nil, errDeserialize("unexpected end of data after header")
	}
	isCoinBase := code&0x01 != 0
	output0Unspent := code&0x02 != 0
	output1Unspent := code&0x04 != 0
	numBitmapBytes := code >> 3
	if !output0Unspent && !output1Unspent {
		numBitmapBytes++
	}
	if uint64(len(serialized[offset:])) < numBitmapBytes {
		return nil, errDeserialize("unexpected end of data for " +
			"unspentness bitmap")
	}
	entry := newUtxoEntry(int32(version), isCoinBase, uint32(blockHeight))
	var outputIndexes []uint32
	if output0Unspent {
		outputIndexes = append(outputIndexes, 0)
	}
	if output1Unspent {
		outputIndexes = append(outputIndexes, 1)
	}
	for i := uint32(0); i < uint32(numBitmapBytes); i++ {
		unspentBits := serialized[offset]
		for j := uint32(0); j < 8; j++ {
			if unspentBits&0x01 != 0 {
				outputNum := 2 + i*8 + j
				outputIndexes = append(outputIndexes, outputNum)
			}
			unspentBits >>= 1
		}
		offset++
	}
	for i, outputIndex := range outputIndexes {
		compAmount, compScript, bytesRead, err := decodeCompressedTxOut(
			serialized[offset:], int32(version))
		if err != nil {
			return nil, errDeserialize(fmt.Sprintf("unable to "+
				"decode utxo at index %d: %v", i, err))
		}
		offset += bytesRead
		entry.sparseOutputs[outputIndex] = &utxoOutput{
			spent:      false,
			compressed: true,
			pkScript:   compScript,
			amount:     int64(compAmount),
		}
	}
	return entry, nil
}
func dbFetchUtxoEntry(dbTx database.Tx, hash *chainhash.Hash) (*UtxoEntry, error) {
	utxoBucket := dbTx.Metadata().Bucket(utxoSetBucketName)
	serializedUtxo := utxoBucket.Get(hash[:])
	if serializedUtxo == nil {
		return nil, nil
	}
	if len(serializedUtxo) == 0 {
		return nil, AssertError(fmt.Sprintf("database contains entry "+
			"for fully spent tx %v", hash))
	}
	entry, err := deserializeUtxoEntry(serializedUtxo)
	if err != nil {
		if isDeserializeErr(err) {
			return nil, database.Error{
				ErrorCode: database.ErrCorruption,
				Description: fmt.Sprintf("corrupt utxo entry "+
					"for %v: %v", hash, err),
			}
		}
		return nil, err
	}
	return entry, nil
}