func CoinIdentifier(hash string, vout int64) string {
	return fmt.Sprintf("%s:%d", hash, vout)
}
CoinIdentifier converts a tx hash and vout into
the canonical CoinIdentifier.Identifier used in
rosetta-bitcoin.
package bitcoin
import (
	"fmt"
	"strings"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/coinbase/rosetta-sdk-go/types"
)
const (
	Blockchain string = "Bitcoin"
	MainnetNetwork string = "Mainnet"
	TestnetNetwork string = "Testnet3"
	Decimals = 8
	SatoshisInBitcoin = 100000000
	InputOpType = "INPUT"
	OutputOpType = "OUTPUT"
	CoinbaseOpType = "COINBASE"
	SuccessStatus = "SUCCESS"
	SkippedStatus = "SKIPPED"
	TransactionHashLength = 64
	NullData = "nulldata"
)
const (
	MinFeeRate            = float64(0.00001)  
	TransactionOverhead   = 12                
	InputSize             = 68                
	OutputOverhead        = 9                 
	P2PKHScriptPubkeySize = 25                
)
var (
	MainnetGenesisBlockIdentifier = &types.BlockIdentifier{
		Hash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
	}
	MainnetParams = &chaincfg.MainNetParams
	MainnetCurrency = &types.Currency{
		Symbol:   "BTC",
		Decimals: Decimals,
	}
	TestnetGenesisBlockIdentifier = &types.BlockIdentifier{
		Hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
	}
	TestnetParams = &chaincfg.TestNet3Params
	TestnetCurrency = &types.Currency{
		Symbol:   "tBTC",
		Decimals: Decimals,
	}
	OperationTypes = []string{
		InputOpType,
		OutputOpType,
		CoinbaseOpType,
	}
	OperationStatuses = []*types.OperationStatus{
		{
			Status:     SuccessStatus,
			Successful: true,
		},
		{
			Status:     SkippedStatus,
			Successful: false,
		},
	}
)
type ScriptPubKey struct {
	ASM          string   `json:"asm"`
	Hex          string   `json:"hex"`
	RequiredSigs int64    `json:"reqSigs,omitempty"`
	Type         string   `json:"type"`
	Addresses    []string `json:"addresses,omitempty"`
}
type ScriptSig struct {
	ASM string `json:"asm"`
	Hex string `json:"hex"`
}
type BlockchainInfo struct {
	Chain         string `json:"chain"`
	Blocks        int64  `json:"blocks"`
	BestBlockHash string `json:"bestblockhash"`
}
type PeerInfo struct {
	Addr           string `json:"addr"`
	Version        int64  `json:"version"`
	SubVer         string `json:"subver"`
	StartingHeight int64  `json:"startingheight"`
	RelayTxes      bool   `json:"relaytxes"`
	LastSend       int64  `json:"lastsend"`
	LastRecv       int64  `json:"lastrecv"`
	BanScore       int64  `json:"banscore"`
	SyncedBlocks   int64  `json:"synced_blocks"`
	SyncedHeaders  int64  `json:"synced_headers"`
}
type Block struct {
	Hash              string  `json:"hash"`
	Height            int64   `json:"height"`
	PreviousBlockHash string  `json:"previousblockhash"`
	Time              int64   `json:"time"`
	MedianTime        int64   `json:"mediantime"`
	Nonce             int64   `json:"nonce"`
	MerkleRoot        string  `json:"merkleroot"`
	Version           int32   `json:"version"`
	Size              int64   `json:"size"`
	Weight            int64   `json:"weight"`
	Bits              string  `json:"bits"`
	Difficulty        float64 `json:"difficulty"`
	Txs []*Transaction `json:"tx"`
}
func (b Block) Metadata() (map[string]interface{}, error) {
	m := &BlockMetadata{
		Nonce:      b.Nonce,
		MerkleRoot: b.MerkleRoot,
		Version:    b.Version,
		Size:       b.Size,
		Weight:     b.Weight,
		MedianTime: b.MedianTime,
		Bits:       b.Bits,
		Difficulty: b.Difficulty,
	}
	return types.MarshalMap(m)
}
type BlockMetadata struct {
	Nonce      int64   `json:"nonce,omitempty"`
	MerkleRoot string  `json:"merkleroot,omitempty"`
	Version    int32   `json:"version,omitempty"`
	Size       int64   `json:"size,omitempty"`
	Weight     int64   `json:"weight,omitempty"`
	MedianTime int64   `json:"mediantime,omitempty"`
	Bits       string  `json:"bits,omitempty"`
	Difficulty float64 `json:"difficulty,omitempty"`
}
type Transaction struct {
	Hex      string `json:"hex"`
	Hash     string `json:"txid"`
	Size     int64  `json:"size"`
	Vsize    int64  `json:"vsize"`
	Version  int32  `json:"version"`
	Locktime int64  `json:"locktime"`
	Weight   int64  `json:"weight"`
	Inputs  []*Input  `json:"vin"`
	Outputs []*Output `json:"vout"`
}
func (t Transaction) Metadata() (map[string]interface{}, error) {
	m := &TransactionMetadata{
		Size:     t.Size,
		Vsize:    t.Vsize,
		Version:  t.Version,
		Locktime: t.Locktime,
		Weight:   t.Weight,
	}
	return types.MarshalMap(m)
}
type TransactionMetadata struct {
	Size     int64 `json:"size,omitempty"`
	Vsize    int64 `json:"vsize,omitempty"`
	Version  int32 `json:"version,omitempty"`
	Locktime int64 `json:"locktime,omitempty"`
	Weight   int64 `json:"weight,omitempty"`
}
type Input struct {
	TxHash      string     `json:"txid"`
	Vout        int64      `json:"vout"`
	ScriptSig   *ScriptSig `json:"scriptSig"`
	Sequence    int64      `json:"sequence"`
	TxInWitness []string   `json:"txinwitness"`
	Coinbase string `json:"coinbase"`
}
func (i Input) Metadata() (map[string]interface{}, error) {
	m := &OperationMetadata{
		ScriptSig:   i.ScriptSig,
		Sequence:    i.Sequence,
		TxInWitness: i.TxInWitness,
		Coinbase:    i.Coinbase,
	}
	return types.MarshalMap(m)
}
type Output struct {
	Value        float64       `json:"value"`
	Index        int64         `json:"n"`
	ScriptPubKey *ScriptPubKey `json:"scriptPubKey"`
}
func (o Output) Metadata() (map[string]interface{}, error) {
	m := &OperationMetadata{
		ScriptPubKey: o.ScriptPubKey,
	}
	return types.MarshalMap(m)
}
type OperationMetadata struct {
	Coinbase string `json:"coinbase,omitempty"`
	ScriptSig   *ScriptSig `json:"scriptsig,omitempty"`
	Sequence    int64      `json:"sequence,omitempty"`
	TxInWitness []string   `json:"txinwitness,omitempty"`
	ScriptPubKey *ScriptPubKey `json:"scriptPubKey,omitempty"`
}
type request struct {
	JSONRPC string        `json:"jsonrpc"`
	ID      int           `json:"id"`
	Method  string        `json:"method"`
	Params  []interface{} `json:"params"`
}
func (r request) GetVersion() string       { return r.JSONRPC }
func (r request) GetID() int               { return r.ID }
func (r request) GetMethod() string        { return r.Method }
func (r request) GetParams() []interface{} { return r.Params }
type jSONRPCResponse interface {
	Err() error
}
type responseError struct {
	Code    int64  `json:"code"`
	Message string `json:"message"`
}
type blockResponse struct {
	Result *Block         `json:"result"`
	Error  *responseError `json:"error"`
}
func (b blockResponse) Err() error {
	if b.Error == nil {
		return nil
	}
	if b.Error.Code == blockNotFoundErrCode {
		return ErrBlockNotFound
	}
	return fmt.Errorf(
		"%w: error JSON RPC response, code: %d, message: %s",
		ErrJSONRPCError,
		b.Error.Code,
		b.Error.Message,
	)
}
type pruneBlockchainResponse struct {
	Result int64          `json:"result"`
	Error  *responseError `json:"error"`
}
func (p pruneBlockchainResponse) Err() error {
	if p.Error == nil {
		return nil
	}
	return fmt.Errorf(
		"%w: error JSON RPC response, code: %d, message: %s",
		ErrJSONRPCError,
		p.Error.Code,
		p.Error.Message,
	)
}
type blockchainInfoResponse struct {
	Result *BlockchainInfo `json:"result"`
	Error  *responseError  `json:"error"`
}
func (b blockchainInfoResponse) Err() error {
	if b.Error == nil {
		return nil
	}
	return fmt.Errorf(
		"%w: error JSON RPC response, code: %d, message: %s",
		ErrJSONRPCError,
		b.Error.Code,
		b.Error.Message,
	)
}
type peerInfoResponse struct {
	Result []*PeerInfo    `json:"result"`
	Error  *responseError `json:"error"`
}
func (p peerInfoResponse) Err() error {
	if p.Error == nil {
		return nil
	}
	return fmt.Errorf(
		"%w: error JSON RPC response, code: %d, message: %s",
		ErrJSONRPCError,
		p.Error.Code,
		p.Error.Message,
	)
}
type blockHashResponse struct {
	Result string         `json:"result"`
	Error  *responseError `json:"error"`
}
func (b blockHashResponse) Err() error {
	if b.Error == nil {
		return nil
	}
	return fmt.Errorf(
		"%w: error JSON RPC response, code: %d, message: %s",
		ErrJSONRPCError,
		b.Error.Code,
		b.Error.Message,
	)
}
type sendRawTransactionResponse struct {
	Result string         `json:"result"`
	Error  *responseError `json:"error"`
}
func (s sendRawTransactionResponse) Err() error {
	if s.Error == nil {
		return nil
	}
	return fmt.Errorf(
		"%w: error JSON RPC response, code: %d, message: %s",
		ErrJSONRPCError,
		s.Error.Code,
		s.Error.Message,
	)
}
type suggestedFeeRate struct {
	FeeRate float64 `json:"feerate"`
}
type suggestedFeeRateResponse struct {
	Result *suggestedFeeRate `json:"result"`
	Error  *responseError    `json:"error"`
}
func (s suggestedFeeRateResponse) Err() error {
	if s.Error == nil {
		return nil
	}
	return fmt.Errorf(
		"%w: error JSON RPC response, code: %d, message: %s",
		ErrJSONRPCError,
		s.Error.Code,
		s.Error.Message,
	)
}
type rawMempoolResponse struct {
	Result []string       `json:"result"`
	Error  *responseError `json:"error"`
}
func (r rawMempoolResponse) Err() error {
	if r.Error == nil {
		return nil
	}
	return fmt.Errorf(
		"%w: error JSON RPC response, code: %d, message: %s",
		ErrJSONRPCError,
		r.Error.Code,
		r.Error.Message,
	)
}