func dbRemoveBlockIndex(dbTx database.Tx, hash *chainhash.Hash, height uint32) error {
	meta := dbTx.Metadata()
	hashIndex := meta.Bucket(hashIndexBucketName)
	if err := hashIndex.Delete(hash[:]); err != nil {
		return err
	}
	var serializedHeight [4]byte
	byteOrder.PutUint32(serializedHeight[:], height)
	heightIndex := meta.Bucket(heightIndexBucketName)
	return heightIndex.Delete(serializedHeight[:])
}
dbRemoveBlockIndex uses an existing database transaction remove block index
entries from the hash to height and height to hash mappings for the provided
values.
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
}
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
}
func dbPutBlockIndex(dbTx database.Tx, hash *chainhash.Hash, height uint32) error {
	var serializedHeight [4]byte
	byteOrder.PutUint32(serializedHeight[:], height)
	meta := dbTx.Metadata()
	hashIndex := meta.Bucket(hashIndexBucketName)
	if err := hashIndex.Put(hash[:], serializedHeight[:]); err != nil {
		return err
	}
	heightIndex := meta.Bucket(heightIndexBucketName)
	return heightIndex.Put(serializedHeight[:], hash[:])
}