github.com/cerberus-wallet/blockbook@v0.3.2/bchain/coins/btc/bitcoinparser.go (about)

     1  package btc
     2  
     3  import (
     4  	"blockbook/bchain"
     5  	"bytes"
     6  	"encoding/binary"
     7  	"encoding/hex"
     8  	"math/big"
     9  	"strconv"
    10  
    11  	vlq "github.com/bsm/go-vlq"
    12  	"github.com/juju/errors"
    13  	"github.com/martinboehm/btcd/blockchain"
    14  	"github.com/martinboehm/btcd/wire"
    15  	"github.com/martinboehm/btcutil"
    16  	"github.com/martinboehm/btcutil/chaincfg"
    17  	"github.com/martinboehm/btcutil/hdkeychain"
    18  	"github.com/martinboehm/btcutil/txscript"
    19  )
    20  
    21  // OutputScriptToAddressesFunc converts ScriptPubKey to bitcoin addresses
    22  type OutputScriptToAddressesFunc func(script []byte) ([]string, bool, error)
    23  
    24  // BitcoinParser handle
    25  type BitcoinParser struct {
    26  	*bchain.BaseParser
    27  	Params                       *chaincfg.Params
    28  	OutputScriptToAddressesFunc  OutputScriptToAddressesFunc
    29  	XPubMagic                    uint32
    30  	XPubMagicSegwitP2sh          uint32
    31  	XPubMagicSegwitNative        uint32
    32  	Slip44                       uint32
    33  	minimumCoinbaseConfirmations int
    34  }
    35  
    36  // NewBitcoinParser returns new BitcoinParser instance
    37  func NewBitcoinParser(params *chaincfg.Params, c *Configuration) *BitcoinParser {
    38  	p := &BitcoinParser{
    39  		BaseParser: &bchain.BaseParser{
    40  			BlockAddressesToKeep: c.BlockAddressesToKeep,
    41  			AmountDecimalPoint:   8,
    42  		},
    43  		Params:                       params,
    44  		XPubMagic:                    c.XPubMagic,
    45  		XPubMagicSegwitP2sh:          c.XPubMagicSegwitP2sh,
    46  		XPubMagicSegwitNative:        c.XPubMagicSegwitNative,
    47  		Slip44:                       c.Slip44,
    48  		minimumCoinbaseConfirmations: c.MinimumCoinbaseConfirmations,
    49  	}
    50  	p.OutputScriptToAddressesFunc = p.outputScriptToAddresses
    51  	return p
    52  }
    53  
    54  // GetChainParams contains network parameters for the main Bitcoin network,
    55  // the regression test Bitcoin network, the test Bitcoin network and
    56  // the simulation test Bitcoin network, in this order
    57  func GetChainParams(chain string) *chaincfg.Params {
    58  	if !chaincfg.IsRegistered(&chaincfg.MainNetParams) {
    59  		chaincfg.RegisterBitcoinParams()
    60  	}
    61  	switch chain {
    62  	case "test":
    63  		return &chaincfg.TestNet3Params
    64  	case "regtest":
    65  		return &chaincfg.RegressionNetParams
    66  	}
    67  	return &chaincfg.MainNetParams
    68  }
    69  
    70  // GetAddrDescFromVout returns internal address representation (descriptor) of given transaction output
    71  func (p *BitcoinParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) {
    72  	ad, err := hex.DecodeString(output.ScriptPubKey.Hex)
    73  	if err != nil {
    74  		return ad, err
    75  	}
    76  	// convert possible P2PK script to P2PKH
    77  	// so that all transactions by given public key are indexed together
    78  	return txscript.ConvertP2PKtoP2PKH(p.Params.Base58CksumHasher, ad)
    79  }
    80  
    81  // GetAddrDescFromAddress returns internal address representation (descriptor) of given address
    82  func (p *BitcoinParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) {
    83  	return p.addressToOutputScript(address)
    84  }
    85  
    86  // GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable
    87  func (p *BitcoinParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) {
    88  	return p.OutputScriptToAddressesFunc(addrDesc)
    89  }
    90  
    91  // GetScriptFromAddrDesc returns output script for given address descriptor
    92  func (p *BitcoinParser) GetScriptFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]byte, error) {
    93  	return addrDesc, nil
    94  }
    95  
    96  // IsAddrDescIndexable returns true if AddressDescriptor should be added to index
    97  // empty or OP_RETURN scripts are not indexed
    98  func (p *BitcoinParser) IsAddrDescIndexable(addrDesc bchain.AddressDescriptor) bool {
    99  	if len(addrDesc) == 0 || addrDesc[0] == txscript.OP_RETURN {
   100  		return false
   101  	}
   102  	return true
   103  }
   104  
   105  // addressToOutputScript converts bitcoin address to ScriptPubKey
   106  func (p *BitcoinParser) addressToOutputScript(address string) ([]byte, error) {
   107  	da, err := btcutil.DecodeAddress(address, p.Params)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	script, err := txscript.PayToAddrScript(da)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	return script, nil
   116  }
   117  
   118  // TryParseOPReturn tries to process OP_RETURN script and return its string representation
   119  func (p *BitcoinParser) TryParseOPReturn(script []byte) string {
   120  	if len(script) > 1 && script[0] == txscript.OP_RETURN {
   121  		// trying 2 variants of OP_RETURN data
   122  		// 1) OP_RETURN OP_PUSHDATA1 <datalen> <data>
   123  		// 2) OP_RETURN <datalen> <data>
   124  		var data []byte
   125  		var l int
   126  		if script[1] == txscript.OP_PUSHDATA1 && len(script) > 2 {
   127  			l = int(script[2])
   128  			data = script[3:]
   129  			if l != len(data) {
   130  				l = int(script[1])
   131  				data = script[2:]
   132  			}
   133  		} else {
   134  			l = int(script[1])
   135  			data = script[2:]
   136  		}
   137  		if l == len(data) {
   138  			var ed string
   139  
   140  			ed = p.tryParseOmni(data)
   141  			if ed != "" {
   142  				return ed
   143  			}
   144  
   145  			isASCII := true
   146  			for _, c := range data {
   147  				if c < 32 || c > 127 {
   148  					isASCII = false
   149  					break
   150  				}
   151  			}
   152  			if isASCII {
   153  				ed = "(" + string(data) + ")"
   154  			} else {
   155  				ed = hex.EncodeToString(data)
   156  			}
   157  			return "OP_RETURN " + ed
   158  		}
   159  	}
   160  	return ""
   161  }
   162  
   163  var omniCurrencyMap = map[uint32]string{
   164  	1:  "Omni",
   165  	2:  "Test Omni",
   166  	31: "TetherUS",
   167  }
   168  
   169  // tryParseOmni tries to extract Omni simple send transaction from script
   170  func (p *BitcoinParser) tryParseOmni(data []byte) string {
   171  
   172  	// currently only simple send transaction version 0 is supported, see
   173  	// https://github.com/OmniLayer/spec#transfer-coins-simple-send
   174  	if len(data) != 20 || data[0] != 'o' {
   175  		return ""
   176  	}
   177  	// omni (4) <tx_version> (2) <tx_type> (2)
   178  	omniHeader := []byte{'o', 'm', 'n', 'i', 0, 0, 0, 0}
   179  	if bytes.Compare(data[0:8], omniHeader) != 0 {
   180  		return ""
   181  	}
   182  
   183  	currencyID := binary.BigEndian.Uint32(data[8:12])
   184  	currency, ok := omniCurrencyMap[currencyID]
   185  	if !ok {
   186  		return ""
   187  	}
   188  	amount := new(big.Int)
   189  	amount.SetBytes(data[12:])
   190  	amountStr := p.AmountToDecimalString(amount)
   191  
   192  	ed := "OMNI Simple Send: " + amountStr + " " + currency + " (#" + strconv.Itoa(int(currencyID)) + ")"
   193  	return ed
   194  }
   195  
   196  // outputScriptToAddresses converts ScriptPubKey to addresses with a flag that the addresses are searchable
   197  func (p *BitcoinParser) outputScriptToAddresses(script []byte) ([]string, bool, error) {
   198  	sc, addresses, _, err := txscript.ExtractPkScriptAddrs(script, p.Params)
   199  	if err != nil {
   200  		return nil, false, err
   201  	}
   202  	rv := make([]string, len(addresses))
   203  	for i, a := range addresses {
   204  		rv[i] = a.EncodeAddress()
   205  	}
   206  	var s bool
   207  	if sc == txscript.PubKeyHashTy || sc == txscript.WitnessV0PubKeyHashTy || sc == txscript.ScriptHashTy || sc == txscript.WitnessV0ScriptHashTy {
   208  		s = true
   209  	} else if len(rv) == 0 {
   210  		or := p.TryParseOPReturn(script)
   211  		if or != "" {
   212  			rv = []string{or}
   213  		}
   214  	}
   215  	return rv, s, nil
   216  }
   217  
   218  // TxFromMsgTx converts bitcoin wire Tx to bchain.Tx
   219  func (p *BitcoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx {
   220  	vin := make([]bchain.Vin, len(t.TxIn))
   221  	for i, in := range t.TxIn {
   222  		if blockchain.IsCoinBaseTx(t) {
   223  			vin[i] = bchain.Vin{
   224  				Coinbase: hex.EncodeToString(in.SignatureScript),
   225  				Sequence: in.Sequence,
   226  			}
   227  			break
   228  		}
   229  		s := bchain.ScriptSig{
   230  			Hex: hex.EncodeToString(in.SignatureScript),
   231  			// missing: Asm,
   232  		}
   233  		vin[i] = bchain.Vin{
   234  			Txid:      in.PreviousOutPoint.Hash.String(),
   235  			Vout:      in.PreviousOutPoint.Index,
   236  			Sequence:  in.Sequence,
   237  			ScriptSig: s,
   238  		}
   239  	}
   240  	vout := make([]bchain.Vout, len(t.TxOut))
   241  	for i, out := range t.TxOut {
   242  		addrs := []string{}
   243  		if parseAddresses {
   244  			addrs, _, _ = p.OutputScriptToAddressesFunc(out.PkScript)
   245  		}
   246  		s := bchain.ScriptPubKey{
   247  			Hex:       hex.EncodeToString(out.PkScript),
   248  			Addresses: addrs,
   249  			// missing: Asm,
   250  			// missing: Type,
   251  		}
   252  		var vs big.Int
   253  		vs.SetInt64(out.Value)
   254  		vout[i] = bchain.Vout{
   255  			ValueSat:     vs,
   256  			N:            uint32(i),
   257  			ScriptPubKey: s,
   258  		}
   259  	}
   260  	tx := bchain.Tx{
   261  		Txid:     t.TxHash().String(),
   262  		Version:  t.Version,
   263  		LockTime: t.LockTime,
   264  		Vin:      vin,
   265  		Vout:     vout,
   266  		// skip: BlockHash,
   267  		// skip: Confirmations,
   268  		// skip: Time,
   269  		// skip: Blocktime,
   270  	}
   271  	return tx
   272  }
   273  
   274  // ParseTx parses byte array containing transaction and returns Tx struct
   275  func (p *BitcoinParser) ParseTx(b []byte) (*bchain.Tx, error) {
   276  	t := wire.MsgTx{}
   277  	r := bytes.NewReader(b)
   278  	if err := t.Deserialize(r); err != nil {
   279  		return nil, err
   280  	}
   281  	tx := p.TxFromMsgTx(&t, true)
   282  	tx.Hex = hex.EncodeToString(b)
   283  	return &tx, nil
   284  }
   285  
   286  // ParseBlock parses raw block to our Block struct
   287  func (p *BitcoinParser) ParseBlock(b []byte) (*bchain.Block, error) {
   288  	w := wire.MsgBlock{}
   289  	r := bytes.NewReader(b)
   290  
   291  	if err := w.Deserialize(r); err != nil {
   292  		return nil, err
   293  	}
   294  
   295  	txs := make([]bchain.Tx, len(w.Transactions))
   296  	for ti, t := range w.Transactions {
   297  		txs[ti] = p.TxFromMsgTx(t, false)
   298  	}
   299  
   300  	return &bchain.Block{
   301  		BlockHeader: bchain.BlockHeader{
   302  			Size: len(b),
   303  			Time: w.Header.Timestamp.Unix(),
   304  		},
   305  		Txs: txs,
   306  	}, nil
   307  }
   308  
   309  // PackTx packs transaction to byte array
   310  func (p *BitcoinParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
   311  	buf := make([]byte, 4+vlq.MaxLen64+len(tx.Hex)/2)
   312  	binary.BigEndian.PutUint32(buf[0:4], height)
   313  	vl := vlq.PutInt(buf[4:4+vlq.MaxLen64], blockTime)
   314  	hl, err := hex.Decode(buf[4+vl:], []byte(tx.Hex))
   315  	return buf[0 : 4+vl+hl], err
   316  }
   317  
   318  // UnpackTx unpacks transaction from byte array
   319  func (p *BitcoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
   320  	height := binary.BigEndian.Uint32(buf)
   321  	bt, l := vlq.Int(buf[4:])
   322  	tx, err := p.ParseTx(buf[4+l:])
   323  	if err != nil {
   324  		return nil, 0, err
   325  	}
   326  	tx.Blocktime = bt
   327  
   328  	return tx, height, nil
   329  }
   330  
   331  // MinimumCoinbaseConfirmations returns minimum number of confirmations a coinbase transaction must have before it can be spent
   332  func (p *BitcoinParser) MinimumCoinbaseConfirmations() int {
   333  	return p.minimumCoinbaseConfirmations
   334  }
   335  
   336  func (p *BitcoinParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (bchain.AddressDescriptor, error) {
   337  	var a btcutil.Address
   338  	var err error
   339  	if extKey.Version() == p.XPubMagicSegwitP2sh {
   340  		// redeemScript <witness version: OP_0><len pubKeyHash: 20><20-byte-pubKeyHash>
   341  		pubKeyHash := btcutil.Hash160(extKey.PubKeyBytes())
   342  		redeemScript := make([]byte, len(pubKeyHash)+2)
   343  		redeemScript[0] = 0
   344  		redeemScript[1] = byte(len(pubKeyHash))
   345  		copy(redeemScript[2:], pubKeyHash)
   346  		hash := btcutil.Hash160(redeemScript)
   347  		a, err = btcutil.NewAddressScriptHashFromHash(hash, p.Params)
   348  	} else if extKey.Version() == p.XPubMagicSegwitNative {
   349  		a, err = btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(extKey.PubKeyBytes()), p.Params)
   350  	} else {
   351  		// default to P2PKH address
   352  		a, err = extKey.Address(p.Params)
   353  	}
   354  	if err != nil {
   355  		return nil, err
   356  	}
   357  	return txscript.PayToAddrScript(a)
   358  }
   359  
   360  // DeriveAddressDescriptors derives address descriptors from given xpub for listed indexes
   361  func (p *BitcoinParser) DeriveAddressDescriptors(xpub string, change uint32, indexes []uint32) ([]bchain.AddressDescriptor, error) {
   362  	extKey, err := hdkeychain.NewKeyFromString(xpub, p.Params.Base58CksumHasher)
   363  	if err != nil {
   364  		return nil, err
   365  	}
   366  	changeExtKey, err := extKey.Child(change)
   367  	if err != nil {
   368  		return nil, err
   369  	}
   370  	ad := make([]bchain.AddressDescriptor, len(indexes))
   371  	for i, index := range indexes {
   372  		indexExtKey, err := changeExtKey.Child(index)
   373  		if err != nil {
   374  			return nil, err
   375  		}
   376  		ad[i], err = p.addrDescFromExtKey(indexExtKey)
   377  		if err != nil {
   378  			return nil, err
   379  		}
   380  	}
   381  	return ad, nil
   382  }
   383  
   384  // DeriveAddressDescriptorsFromTo derives address descriptors from given xpub for addresses in index range
   385  func (p *BitcoinParser) DeriveAddressDescriptorsFromTo(xpub string, change uint32, fromIndex uint32, toIndex uint32) ([]bchain.AddressDescriptor, error) {
   386  	if toIndex <= fromIndex {
   387  		return nil, errors.New("toIndex<=fromIndex")
   388  	}
   389  	extKey, err := hdkeychain.NewKeyFromString(xpub, p.Params.Base58CksumHasher)
   390  	if err != nil {
   391  		return nil, err
   392  	}
   393  	changeExtKey, err := extKey.Child(change)
   394  	if err != nil {
   395  		return nil, err
   396  	}
   397  	ad := make([]bchain.AddressDescriptor, toIndex-fromIndex)
   398  	for index := fromIndex; index < toIndex; index++ {
   399  		indexExtKey, err := changeExtKey.Child(index)
   400  		if err != nil {
   401  			return nil, err
   402  		}
   403  		ad[index-fromIndex], err = p.addrDescFromExtKey(indexExtKey)
   404  		if err != nil {
   405  			return nil, err
   406  		}
   407  	}
   408  	return ad, nil
   409  }
   410  
   411  // DerivationBasePath returns base path of xpub
   412  func (p *BitcoinParser) DerivationBasePath(xpub string) (string, error) {
   413  	extKey, err := hdkeychain.NewKeyFromString(xpub, p.Params.Base58CksumHasher)
   414  	if err != nil {
   415  		return "", err
   416  	}
   417  	var c, bip string
   418  	cn := extKey.ChildNum()
   419  	if cn >= 0x80000000 {
   420  		cn -= 0x80000000
   421  		c = "'"
   422  	}
   423  	c = strconv.Itoa(int(cn)) + c
   424  	if extKey.Depth() != 3 {
   425  		return "unknown/" + c, nil
   426  	}
   427  	if extKey.Version() == p.XPubMagicSegwitP2sh {
   428  		bip = "49"
   429  	} else if extKey.Version() == p.XPubMagicSegwitNative {
   430  		bip = "84"
   431  	} else {
   432  		bip = "44"
   433  	}
   434  	return "m/" + bip + "'/" + strconv.Itoa(int(p.Slip44)) + "'/" + c, nil
   435  }