func ParsePurchaseScript(script []byte) (*pb.Purchase, error) {
	data, err := parseDataScript(script)
	if err != nil {
		return nil, err
	}
	if data[0] != purchase {
		return nil, errors.Err("the first byte must be 'P'(0x50) to be a purchase script")
	}
	purchase := &pb.Purchase{}
	err = proto.Unmarshal(data[1:], purchase)
	if err != nil {
		return nil, errors.Err(err)
	}
	return purchase, nil
}
ParsePurchaseScript returns the purchase from script bytes or errors if invalid
package lbrycrd
import (
	"encoding/binary"
	"encoding/hex"
	"github.com/golang/protobuf/proto"
	"github.com/lbryio/chainquery/global"
	"github.com/lbryio/chainquery/util"
	"github.com/lbryio/lbry.go/extras/errors"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcutil"
	pb "github.com/lbryio/types/v2/go"
	log "github.com/sirupsen/logrus"
)
const (
	opClaimName    = 0xb5  
	opSupportClaim = 0xb6  
	opUpdateClaim  = 0xb7  
	opReturn       = 0x6a  
	purchase       = 0x50  
	opDup          = 0x76  
	opChecksig     = 0xac  
	opEqualverify  = 0x88  
	opHash160      = 0xa9  
	opPushdata1    = 0x4c  
	opPushdata2    = 0x4d  
	opPushdata4    = 0x4e  
	p2SH   = "scripthash"             
	p2PK   = "pubkey"                 
	p2PKH  = "pubkeyhash"             
	p2WPKH = "witness_v0_keyhash"     
	p2WSH  = "witness_v0_scripthash"  
	NonStandard = "nonstandard"
	NullData = "nulldata"
	lbrycrdMainPubkeyPrefix    = byte(85)
	lbrycrdMainScriptPrefix    = byte(122)
	lbrycrdTestnetPubkeyPrefix = byte(111)
	lbrycrdTestnetScriptPrefix = byte(196)
	lbrycrdRegtestPubkeyPrefix = byte(111)
	lbrycrdRegtestScriptPrefix = byte(196)
	lbrycrdMain    = "lbrycrd_main"
	lbrycrdTestnet = "lbrycrd_testnet"
	lbrycrdRegtest = "lbrycrd_regtest"
)
var mainNetParams = chaincfg.Params{
	PubKeyHashAddrID: lbrycrdMainPubkeyPrefix,
	ScriptHashAddrID: lbrycrdMainScriptPrefix,
	PrivateKeyID:     0x1c,
	Bech32HRPSegwit:  "lbc",
}
var testNetParams = chaincfg.Params{
	PubKeyHashAddrID: lbrycrdTestnetPubkeyPrefix,
	ScriptHashAddrID: lbrycrdTestnetScriptPrefix,
	PrivateKeyID:     0x1c,
	Bech32HRPSegwit:  "tlbc",
}
var regTestNetParams = chaincfg.Params{
	PubKeyHashAddrID: lbrycrdRegtestPubkeyPrefix,
	ScriptHashAddrID: lbrycrdRegtestScriptPrefix,
	PrivateKeyID:     0x1c,
	Bech32HRPSegwit:  "rlbc",
}
var paramsMap = map[string]chaincfg.Params{lbrycrdMain: mainNetParams, lbrycrdTestnet: testNetParams, lbrycrdRegtest: regTestNetParams}
func GetChainParams() (*chaincfg.Params, error) {
	chainParams, ok := paramsMap[global.BlockChainName]
	if !ok {
		return nil, errors.Err("unknown chain name %s", global.BlockChainName)
	}
	return &chainParams, nil
}
func IsClaimScript(script []byte) bool {
	return script[0] == opSupportClaim ||
		script[0] == opClaimName ||
		script[0] == opUpdateClaim
}
func IsClaimNameScript(script []byte) bool {
	if len(script) > 0 {
		return script[0] == opClaimName
	}
	log.Error("script is nil or length 0!")
	return false
}
func IsClaimSupportScript(script []byte) bool {
	if len(script) > 0 {
		return script[0] == opSupportClaim
	}
	return false
}
func IsClaimUpdateScript(script []byte) bool {
	if len(script) > 0 {
		return script[0] == opUpdateClaim
	}
	return false
}
func IsPurchaseScript(script []byte) bool {
	if len(script) > 2 {
		if script[0] == opReturn && script[2] == purchase {
			_, err := ParsePurchaseScript(script)
			return err == nil
		}
	}
	return false
}
func ParseClaimNameScript(script []byte) (name string, value []byte, pubkeyscript []byte, err error) {
	nameBytesToRead := int(script[1])
	nameStart := 2
	if nameBytesToRead == opPushdata1 {
		nameBytesToRead = int(script[2])
		nameStart = 3
	} else if nameBytesToRead > opPushdata1 {
		panic(errors.Base("Bytes to read is more than next byte! "))
	}
	nameEnd := nameStart + nameBytesToRead
	name = string(script[nameStart:nameEnd])
	dataPushType := int(script[nameEnd])
	valueBytesToRead := int(script[nameEnd])
	valueStart := nameEnd + 1
	if dataPushType == opPushdata1 {
		valueBytesToRead = int(script[nameEnd+1])
		valueStart = nameEnd + 2
	} else if dataPushType == opPushdata2 {
		valueStart = nameEnd + 3
		valueBytesToRead = int(binary.LittleEndian.Uint16(script[nameEnd+1 : valueStart]))
	} else if dataPushType == opPushdata4 {
		valueStart = nameEnd + 5
		valueBytesToRead = int(binary.LittleEndian.Uint32(script[nameEnd+2 : valueStart]))
	}
	valueEnd := valueStart + valueBytesToRead
	value = script[valueStart:valueEnd]
	pksStart := valueEnd + 2          
	pubkeyscript = script[pksStart:]  
	return name, value, pubkeyscript, err
}
func ParseClaimSupportScript(script []byte) (name string, claimid string, pubkeyscript []byte, err error) {
	nameBytesToRead := int(script[1])
	nameStart := 2
	if nameBytesToRead == opPushdata1 {
		nameBytesToRead = int(script[2])
		nameStart = 3
	} else if nameBytesToRead > opPushdata1 {
		err = errors.Err("Bytes to read is more than next byte! ")
		return
	}
	nameEnd := nameStart + nameBytesToRead
	name = string(script[nameStart:nameEnd])
	claimidBytesToRead := int(script[nameEnd])
	claimidStart := nameEnd + 1
	claimidEnd := claimidStart + claimidBytesToRead
	bytes := util.ReverseBytes(script[claimidStart:claimidEnd])
	claimid = hex.EncodeToString(bytes)
	pksStart := claimidEnd + 2        
	pubkeyscript = script[pksStart:]  
	return
}
func ParseClaimUpdateScript(script []byte) (name string, claimid string, value []byte, pubkeyscript []byte, err error) {
	nameBytesToRead := int(script[1])
	nameStart := 2
	if nameBytesToRead == opPushdata1 {
		nameBytesToRead = int(script[2])
		nameStart = 3
	} else if nameBytesToRead > opPushdata1 {
		err = errors.Err("ParseClaimUpdateScript: Bytes to read is more than next byte! ")
		return
	}
	nameEnd := nameStart + nameBytesToRead
	name = string(script[nameStart:nameEnd])
	claimidBytesToRead := int(script[nameEnd])
	claimidStart := nameEnd + 1
	claimidEnd := claimidStart + claimidBytesToRead
	bytes := util.ReverseBytes(script[claimidStart:claimidEnd])
	claimid = hex.EncodeToString(bytes)
	dataPushType := int(script[claimidEnd])
	valueBytesToRead := int(script[claimidEnd])
	valueStart := claimidEnd + 1
	if dataPushType == opPushdata1 {
		valueBytesToRead = int(script[claimidEnd+1])
		valueStart = claimidEnd + 2
	} else if dataPushType == opPushdata2 {
		valueStart = claimidEnd + 3
		valueBytesToRead = int(binary.LittleEndian.Uint16(script[claimidEnd+1 : valueStart]))
	} else if dataPushType == opPushdata4 {
		valueStart = claimidEnd + 5
		valueBytesToRead = int(binary.LittleEndian.Uint32(script[claimidEnd+2 : valueStart]))
	}
	valueEnd := valueStart + valueBytesToRead
	value = script[valueStart:valueEnd]
	pksStart := valueEnd + 2          
	pubkeyscript = script[pksStart:]  
	return name, claimid, value, pubkeyscript, err
}
var ErrNotClaimScript = errors.Base("Script is not a claim script!")
func GetPubKeyScriptFromClaimPKS(script []byte) (pubkeyscript []byte, err error) {
	if IsClaimScript(script) {
		if IsClaimNameScript(script) {
			_, _, pubkeyscript, err = ParseClaimNameScript(script)
			if err != nil {
				return nil, errors.Err(err)
			}
			return pubkeyscript, nil
		} else if IsClaimUpdateScript(script) {
			_, _, _, pubkeyscript, err = ParseClaimUpdateScript(script)
			if err != nil {
				return
			}
			return
		} else if IsClaimSupportScript(script) {
			_, _, pubkeyscript, err = ParseClaimSupportScript(script)
			if err != nil {
				return
			}
			return
		}
	} else {
		err = ErrNotClaimScript
	}
	return
}
func GetAddressFromPublicKeyScript(script []byte) (address string) {
	chainParams, err := GetChainParams()
	if err != nil {
		return ""
	}
	_, BTCAddress, _, err := txscript.ExtractPkScriptAddrs(script, chainParams)
	if len(BTCAddress) < 1 {
		return ""
	}
	address = BTCAddress[0].EncodeAddress()
	return address
}
func getPublicKeyScriptType(script []byte) string {
	if isPayToPublicKey(script) {
		return p2PK
	} else if isPayToPublicKeyHashScript(script) {
		return p2PKH
	} else if isPayToScriptHashScript(script) {
		return p2SH
	} else if txscript.IsPayToWitnessPubKeyHash(script) {
		return p2WPKH
	} else if txscript.IsPayToWitnessScriptHash(script) {
		return p2WSH
	}
	return NonStandard
}
func isPayToScriptHashScript(script []byte) bool {
	if len(script) > 0 {
		return script[0] == opUpdateClaim
	}
	return false
}
func isPayToPublicKey(script []byte) bool {
	return script[len(script)-1] == opChecksig &&
		script[len(script)-2] == opEqualverify &&
		script[0] != opDup
}
func isPayToPublicKeyHashScript(script []byte) bool {
	return len(script) > 0 &&
		script[0] == opDup &&
		script[1] == opHash160
}
func getAddressFromP2PK(hexstring string) (string, error) {
	hexstringBytes, err := hex.DecodeString(hexstring)
	if err != nil {
		return "", err
	}
	chainParams, err := GetChainParams()
	if err != nil {
		return "", errors.Err(err)
	}
	addr, err := btcutil.NewAddressPubKey(hexstringBytes, chainParams)
	if err != nil {
		return "", err
	}
	address := addr.EncodeAddress()
	return address, nil
}
func getAddressFromP2PKH(hexstring string) (string, error) {
	hexstringBytes, err := hex.DecodeString(hexstring)
	if err != nil {
		return "", err
	}
	chainParams, err := GetChainParams()
	if err != nil {
		return "", errors.Err(err)
	}
	addr, err := btcutil.NewAddressPubKeyHash(hexstringBytes, chainParams)
	if err != nil {
		return "", err
	}
	address := addr.EncodeAddress()
	return address, nil
}
func getAddressFromP2SH(hexstring string) (string, error) {
	hexstringBytes, err := hex.DecodeString(hexstring)
	if err != nil {
		return "", err
	}
	chainParams, err := GetChainParams()
	if err != nil {
		return "", errors.Err(err)
	}
	addr, err := btcutil.NewAddressScriptHashFromHash(hexstringBytes, chainParams)
	if err != nil {
		return "", err
	}
	address := addr.EncodeAddress()
	return address, nil
}
func getAddressFromP2WPKH(hexstring string) (string, error) {
	witnessProgram, err := hex.DecodeString(hexstring)
	if err != nil {
		return "", err
	}
	chainParams, err := GetChainParams()
	if err != nil {
		return "", errors.Err(err)
	}
	addr, err := btcutil.NewAddressWitnessPubKeyHash(witnessProgram, chainParams)
	if err != nil {
		return "", err
	}
	address := addr.EncodeAddress()
	return address, nil
}
func getAddressFromP2WSH(hexstring string) (string, error) {
	witnessProgram, err := hex.DecodeString(hexstring)
	if err != nil {
		return "", err
	}
	chainParams, err := GetChainParams()
	if err != nil {
		return "", errors.Err(err)
	}
	addr, err := btcutil.NewAddressWitnessScriptHash(witnessProgram, chainParams)
	if err != nil {
		return "", err
	}
	address := addr.EncodeAddress()
	return address, nil
}
func parseDataScript(script []byte) ([]byte, error) {
	if len(script) <= 1 {
		return nil, errors.Err("there is no script to parse")
	}
	if script[0] != opReturn {
		return nil, errors.Err("the first byte of script must be an OP_RETURN to quality as un-spendable data")
	}
	dataBytesToRead := int(script[1])
	if (len(script) - dataBytesToRead - 2) != 0 {
		return nil, errors.Err("supposed to have %d bytes to read but the script is %d bytes", dataBytesToRead, len(script))
	}
	return script[2:], nil
}