func encodeFlags(isCoinBase bool, hasExpiry bool, txType stake.TxType,
	fullySpent bool) byte {
	b := uint8(txType)
	b <<= txTypeShift
	if isCoinBase {
		b |= 0x01  
	}
	if hasExpiry {
		b |= 0x02  
	}
	if fullySpent {
		b |= 0x10  
	}
	return b
}
encodeFlags encodes transaction flags into a single byte.
package blockchain
import (
	"fmt"
	"github.com/HcashOrg/hcashd/blockchain/stake"
	"github.com/HcashOrg/hcashd/chaincfg/chainec"
	"github.com/HcashOrg/hcashd/txscript"
	"github.com/HcashOrg/hcashd/crypto/bliss"
	"github.com/HcashOrg/hcashd/crypto/lms"
)
const currentCompressionVersion = 1
func serializeSizeVLQ(n uint64) int {
	size := 1
	for ; n > 0x7f; n = (n >> 7) - 1 {
		size++
	}
	return size
}
func putVLQ(target []byte, n uint64) int {
	offset := 0
	for ; ; offset++ {
		highBitMask := byte(0x80)
		if offset == 0 {
			highBitMask = 0x00
		}
		target[offset] = byte(n&0x7f) | highBitMask
		if n <= 0x7f {
			break
		}
		n = (n >> 7) - 1
	}
	for i, j := 0, offset; i < j; i, j = i+1, j-1 {
		target[i], target[j] = target[j], target[i]
	}
	return offset + 1
}
func deserializeVLQ(serialized []byte) (uint64, int) {
	var n uint64
	var size int
	for _, val := range serialized {
		size++
		n = (n << 7) | uint64(val&0x7f)
		if val&0x80 != 0x80 {
			break
		}
		n++
	}
	return n, size
}
const (
	cstPayToPubKeyHash = 0
	cstPayToScriptHash = 1
	cstPayToPubKeyCompEven = 2
	cstPayToPubKeyCompOdd = 3
	cstPayToPubKeyUncompEven = 4
	cstPayToPubKeyUncompOdd = 5
	cstPayToPubKeyHashBliss = 6
	cstPayToPubKeyHashLms = 7
	numSpecialScripts = 64
)
func isPubKeyHash(script []byte) (bool, []byte) {
	if len(script) == 25 && script[0] == txscript.OP_DUP &&
		script[1] == txscript.OP_HASH160 &&
		script[2] == txscript.OP_DATA_20 &&
		script[23] == txscript.OP_EQUALVERIFY &&
		script[24] == txscript.OP_CHECKSIG {
		return true, script[3:23]
	}
	return false, nil
}
func isPubKeyHashAlt(script []byte) (bool, []byte, byte) {
	if len(script) == 26 && script[0] == txscript.OP_DUP &&
		script[1] == txscript.OP_HASH160 &&
		script[2] == txscript.OP_DATA_20 &&
		script[23] == txscript.OP_EQUALVERIFY &&
		isOneByteMaxDataPush(script[24]) &&
		script[25] == txscript.OP_CHECKSIGALT {
		return true, script[3:23], script[24]
	}
	return false, nil, 0x00
}
func isOneByteMaxDataPush(op byte) bool {
	return op == txscript.OP_1 ||
		op == txscript.OP_2 ||
		op == txscript.OP_3 ||
		op == txscript.OP_4 ||
		op == txscript.OP_5 ||
		op == txscript.OP_6 ||
		op == txscript.OP_7 ||
		op == txscript.OP_8 ||
		op == txscript.OP_9 ||
		op == txscript.OP_10 ||
		op == txscript.OP_11 ||
		op == txscript.OP_12 ||
		op == txscript.OP_13 ||
		op == txscript.OP_14 ||
		op == txscript.OP_15 ||
		op == txscript.OP_16 ||
		op == txscript.OP_DATA_1
}
func isScriptHash(script []byte) (bool, []byte) {
	if len(script) == 23 && script[0] == txscript.OP_HASH160 &&
		script[1] == txscript.OP_DATA_20 &&
		script[22] == txscript.OP_EQUAL {
		return true, script[2:22]
	}
	return false, nil
}
func isPubKey(script []byte) (bool, []byte) {
	if len(script) == 35 && script[0] == txscript.OP_DATA_33 &&
		script[34] == txscript.OP_CHECKSIG && (script[1] == 0x02 ||
		script[1] == 0x03) {
		serializedPubKey := script[1:34]
		_, err := chainec.Secp256k1.ParsePubKey(serializedPubKey)
		if err == nil {
			return true, serializedPubKey
		}
	}
	if len(script) == 67 && script[0] == txscript.OP_DATA_65 &&
		script[66] == txscript.OP_CHECKSIG && script[1] == 0x04 {
		serializedPubKey := script[1:66]
		_, err := chainec.Secp256k1.ParsePubKey(serializedPubKey)
		if err == nil {
			return true, serializedPubKey
		}
	}
	return false, nil
}
func compressedScriptSize(scriptVersion uint16, pkScript []byte,
	compressionVersion uint32) int {
	if valid, _ := isPubKeyHash(pkScript); valid {
		return 21
	}
	if valid, _, _ := isPubKeyHashAlt(pkScript); valid {
		return 21
	}
	if valid, _ := isScriptHash(pkScript); valid {
		return 21
	}
	if valid, _ := isPubKey(pkScript); valid {
		return 33
	}
	return serializeSizeVLQ(uint64(len(pkScript)+numSpecialScripts)) +
		len(pkScript)
}
func decodeCompressedScriptSize(serialized []byte, compressionVersion uint32) int {
	scriptSize, bytesRead := deserializeVLQ(serialized)
	if bytesRead == 0 {
		return 0
	}
	switch scriptSize {
	case cstPayToPubKeyHash:
		return 21
	case cstPayToScriptHash:
		return 21
	case cstPayToPubKeyHashBliss:
		return 21
	case cstPayToPubKeyHashLms:
		return 21
	case cstPayToPubKeyCompEven, cstPayToPubKeyCompOdd,
		cstPayToPubKeyUncompEven, cstPayToPubKeyUncompOdd:
		return 33
	}
	scriptSize -= numSpecialScripts
	scriptSize += uint64(bytesRead)
	return int(scriptSize)
}
func putCompressedScript(target []byte, scriptVersion uint16, pkScript []byte,
	compressionVersion uint32) int {
	if len(target) == 0 {
		target[0] = 0x00
		return 1
	}
	if valid, hash := isPubKeyHash(pkScript); valid {
		target[0] = cstPayToPubKeyHash
		copy(target[1:21], hash)
		return 21
	}
	if valid, hash := isScriptHash(pkScript); valid {
		target[0] = cstPayToScriptHash
		copy(target[1:21], hash)
		return 21
	}
	if valid, serializedPubKey := isPubKey(pkScript); valid {
		pubKeyFormat := serializedPubKey[0]
		switch pubKeyFormat {
		case 0x02, 0x03:
			if pubKeyFormat == 0x02 {
				target[0] = cstPayToPubKeyCompEven
			}
			if pubKeyFormat == 0x03 {
				target[0] = cstPayToPubKeyCompOdd
			}
			copy(target[1:33], serializedPubKey[1:33])
			return 33
		case 0x04:
			target[0] = cstPayToPubKeyUncompEven
			if (serializedPubKey[64] & 0x01) == 0x01 {
				target[0] = cstPayToPubKeyUncompOdd
			}
			copy(target[1:33], serializedPubKey[1:33])
			return 33
		}
	}
	if valid, hash, tp := isPubKeyHashAlt(pkScript); valid {
		target[0] = cstPayToPubKeyHashBliss
		if tp == 0x05 || tp == 0x55{
			target[0] = cstPayToPubKeyHashLms
		}
		copy(target[1:21], hash)
		return 21
	}
	encodedSize := uint64(len(pkScript) + numSpecialScripts)
	vlqSizeLen := putVLQ(target, encodedSize)
	copy(target[vlqSizeLen:], pkScript)
	return vlqSizeLen + len(pkScript)
}
func decompressScript(compressedPkScript []byte,
	compressionVersion uint32) []byte {
	if len(compressedPkScript) == 0 {
		return nil
	}
	encodedScriptSize, bytesRead := deserializeVLQ(compressedPkScript)
	switch encodedScriptSize {
	case cstPayToPubKeyHash:
		pkScript := make([]byte, 25)
		pkScript[0] = txscript.OP_DUP
		pkScript[1] = txscript.OP_HASH160
		pkScript[2] = txscript.OP_DATA_20
		copy(pkScript[3:], compressedPkScript[bytesRead:bytesRead+20])
		pkScript[23] = txscript.OP_EQUALVERIFY
		pkScript[24] = txscript.OP_CHECKSIG
		return pkScript
	case cstPayToScriptHash:
		pkScript := make([]byte, 23)
		pkScript[0] = txscript.OP_HASH160
		pkScript[1] = txscript.OP_DATA_20
		copy(pkScript[2:], compressedPkScript[bytesRead:bytesRead+20])
		pkScript[22] = txscript.OP_EQUAL
		return pkScript
	case cstPayToPubKeyCompEven, cstPayToPubKeyCompOdd:
		pkScript := make([]byte, 35)
		pkScript[0] = txscript.OP_DATA_33
		oddness := byte(0x02)
		if encodedScriptSize == cstPayToPubKeyCompOdd {
			oddness = 0x03
		}
		pkScript[1] = oddness
		copy(pkScript[2:], compressedPkScript[bytesRead:bytesRead+32])
		pkScript[34] = txscript.OP_CHECKSIG
		return pkScript
	case cstPayToPubKeyUncompEven, cstPayToPubKeyUncompOdd:
		compressedKey := make([]byte, 33)
		oddness := byte(0x02)
		if encodedScriptSize == cstPayToPubKeyUncompOdd {
			oddness = 0x03
		}
		compressedKey[0] = oddness
		copy(compressedKey[1:], compressedPkScript[1:])
		key, err := chainec.Secp256k1.ParsePubKey(compressedKey)
		if err != nil {
			return nil
		}
		pkScript := make([]byte, 67)
		pkScript[0] = txscript.OP_DATA_65
		copy(pkScript[1:], key.SerializeUncompressed())
		pkScript[66] = txscript.OP_CHECKSIG
		return pkScript
	case cstPayToPubKeyHashBliss:
		pkScript := make([]byte, 26)
		pkScript[0] = txscript.OP_DUP
		pkScript[1] = txscript.OP_HASH160
		pkScript[2] = txscript.OP_DATA_20
		copy(pkScript[3:], compressedPkScript[bytesRead:bytesRead+20])
		pkScript[23] = txscript.OP_EQUALVERIFY
		pkScript[24] = byte(bliss.BSTypeBliss)
		pkScript[25] = txscript.OP_CHECKSIGALT
		return pkScript
	case cstPayToPubKeyHashLms:
		pkScript := make([]byte, 26)
		pkScript[0] = txscript.OP_DUP
		pkScript[1] = txscript.OP_HASH160
		pkScript[2] = txscript.OP_DATA_20
		copy(pkScript[3:], compressedPkScript[bytesRead:bytesRead+20])
		pkScript[23] = txscript.OP_EQUALVERIFY
		pkScript[24] = byte(lms.LMSTypeLMS)
		pkScript[25] = txscript.OP_CHECKSIGALT
		return pkScript
	}
	scriptSize := int(encodedScriptSize - numSpecialScripts)
	pkScript := make([]byte, scriptSize)
	copy(pkScript, compressedPkScript[bytesRead:bytesRead+scriptSize])
	return pkScript
}
func compressTxOutAmount(amount uint64) uint64 {
	if amount == 0 {
		return 0
	}
	exponent := uint64(0)
	for amount%10 == 0 && exponent < 9 {
		amount /= 10
		exponent++
	}
	if exponent < 9 {
		lastDigit := amount % 10
		amount /= 10
		return 1 + 10*(9*amount+lastDigit-1) + exponent
	}
	return 10 + 10*(amount-1)
}
func decompressTxOutAmount(amount uint64) uint64 {
	if amount == 0 {
		return 0
	}
	amount--
	exponent := amount % 10
	amount /= 10
	n := uint64(0)
	if exponent < 9 {
		lastDigit := amount%9 + 1
		amount /= 9
		n = amount*10 + lastDigit
	} else {
		n = amount + 1
	}
	for ; exponent > 0; exponent-- {
		n *= 10
	}
	return n
}
func compressedTxOutSize(amount uint64, scriptVersion uint16, pkScript []byte,
	compressionVersion uint32, preCompressed bool, hasAmount bool) int {
	scriptVersionSize := serializeSizeVLQ(uint64(scriptVersion))
	if preCompressed && !hasAmount {
		return scriptVersionSize + len(pkScript)
	}
	if preCompressed && hasAmount {
		return scriptVersionSize + serializeSizeVLQ(compressTxOutAmount(amount)) +
			len(pkScript)
	}
	if !preCompressed && !hasAmount {
		return scriptVersionSize + compressedScriptSize(scriptVersion,
			pkScript, compressionVersion)
	}
	return scriptVersionSize + serializeSizeVLQ(compressTxOutAmount(amount)) +
		compressedScriptSize(scriptVersion, pkScript, compressionVersion)
}
func putCompressedTxOut(target []byte, amount uint64, scriptVersion uint16,
	pkScript []byte, compressionVersion uint32, preCompressed bool,
	hasAmount bool) int {
	if preCompressed && hasAmount {
		offset := putVLQ(target, compressTxOutAmount(amount))
		offset += putVLQ(target[offset:], uint64(scriptVersion))
		copy(target[offset:], pkScript)
		return offset + len(pkScript)
	}
	if preCompressed && !hasAmount {
		offset := putVLQ(target, uint64(scriptVersion))
		copy(target[offset:], pkScript)
		return offset + len(pkScript)
	}
	if !preCompressed && !hasAmount {
		offset := putVLQ(target, uint64(scriptVersion))
		offset += putCompressedScript(target[offset:], scriptVersion, pkScript,
			compressionVersion)
		return offset
	}
	offset := putVLQ(target, compressTxOutAmount(amount))
	offset += putVLQ(target[offset:], uint64(scriptVersion))
	offset += putCompressedScript(target[offset:], scriptVersion, pkScript,
		compressionVersion)
	return offset
}
func decodeCompressedTxOut(serialized []byte, compressionVersion uint32,
	hasAmount bool) (int64, uint16, []byte, int, error) {
	var amount int64
	var bytesRead int
	var offset int
	if hasAmount {
		var compressedAmount uint64
		compressedAmount, bytesRead = deserializeVLQ(serialized)
		if bytesRead >= len(serialized) {
			return 0, 0, nil, bytesRead, errDeserialize("unexpected end of " +
				"data after compressed amount")
		}
		amount = int64(decompressTxOutAmount(compressedAmount))
		offset += bytesRead
	}
	var scriptVersion uint64
	scriptVersion, bytesRead = deserializeVLQ(serialized[offset:])
	offset += bytesRead
	scriptSize := decodeCompressedScriptSize(serialized[offset:],
		compressionVersion)
	if scriptSize < 0 {
		return 0, 0, nil, offset, errDeserialize("negative script size")
	}
	if len(serialized[offset:]) < scriptSize {
		return 0, 0, nil, offset, errDeserialize(fmt.Sprintf("unexpected end of "+
			"data after script size (got %v, need %v)", len(serialized[offset:]),
			scriptSize))
	}
	compressedScript := make([]byte, scriptSize)
	copy(compressedScript, serialized[offset:offset+scriptSize])
	return amount, uint16(scriptVersion), compressedScript,
		offset + scriptSize, nil
}
const (
	txTypeBitmask = 0x0c
	txTypeShift = 2
)