github.com/bchainhub/blockbook@v0.3.2/bchain/coins/dcr/decredparser.go (about)

     1  package dcr
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"math"
     9  	"math/big"
    10  	"strconv"
    11  
    12  	"blockbook/bchain"
    13  	"blockbook/bchain/coins/btc"
    14  	"blockbook/bchain/coins/utils"
    15  
    16  	cfg "github.com/decred/dcrd/chaincfg"
    17  	"github.com/decred/dcrd/chaincfg/chainhash"
    18  	"github.com/decred/dcrd/hdkeychain"
    19  	"github.com/decred/dcrd/txscript"
    20  	"github.com/juju/errors"
    21  	"github.com/martinboehm/btcd/wire"
    22  	"github.com/martinboehm/btcutil/base58"
    23  	"github.com/martinboehm/btcutil/chaincfg"
    24  )
    25  
    26  const (
    27  	// MainnetMagic is mainnet network constant
    28  	MainnetMagic wire.BitcoinNet = 0xd9b400f9
    29  	// TestnetMagic is testnet network constant
    30  	TestnetMagic wire.BitcoinNet = 0xb194aa75
    31  )
    32  
    33  var (
    34  	// MainNetParams are parser parameters for mainnet
    35  	MainNetParams chaincfg.Params
    36  	// TestNet3Params are parser parameters for testnet
    37  	TestNet3Params chaincfg.Params
    38  )
    39  
    40  func init() {
    41  	MainNetParams = chaincfg.MainNetParams
    42  	MainNetParams.Net = MainnetMagic
    43  	MainNetParams.PubKeyHashAddrID = []byte{0x07, 0x3f}
    44  	MainNetParams.ScriptHashAddrID = []byte{0x07, 0x1a}
    45  
    46  	TestNet3Params = chaincfg.TestNet3Params
    47  	TestNet3Params.Net = TestnetMagic
    48  	TestNet3Params.PubKeyHashAddrID = []byte{0x0f, 0x21}
    49  	TestNet3Params.ScriptHashAddrID = []byte{0x0e, 0xfc}
    50  }
    51  
    52  // DecredParser handle
    53  type DecredParser struct {
    54  	*btc.BitcoinParser
    55  	baseParser *bchain.BaseParser
    56  	netConfig  *cfg.Params
    57  }
    58  
    59  // NewDecredParser returns new DecredParser instance
    60  func NewDecredParser(params *chaincfg.Params, c *btc.Configuration) *DecredParser {
    61  	d := &DecredParser{
    62  		BitcoinParser: btc.NewBitcoinParser(params, c),
    63  		baseParser:    &bchain.BaseParser{},
    64  	}
    65  
    66  	switch d.BitcoinParser.Params.Name {
    67  	case "testnet3":
    68  		d.netConfig = &cfg.TestNet3Params
    69  	default:
    70  		d.netConfig = &cfg.MainNetParams
    71  	}
    72  	return d
    73  }
    74  
    75  // GetChainParams contains network parameters for the main Decred network,
    76  // and the test Decred network.
    77  func GetChainParams(chain string) *chaincfg.Params {
    78  	var param *chaincfg.Params
    79  
    80  	switch chain {
    81  	case "testnet3":
    82  		param = &TestNet3Params
    83  	default:
    84  		param = &MainNetParams
    85  	}
    86  
    87  	if !chaincfg.IsRegistered(param) {
    88  		if err := chaincfg.Register(param); err != nil {
    89  			panic(err)
    90  		}
    91  	}
    92  	return param
    93  }
    94  
    95  // ParseBlock parses raw block to our Block struct.
    96  func (p *DecredParser) ParseBlock(b []byte) (*bchain.Block, error) {
    97  	r := bytes.NewReader(b)
    98  	h := wire.BlockHeader{}
    99  	if err := h.Deserialize(r); err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	if (h.Version & utils.VersionAuxpow) != 0 {
   104  		if err := utils.SkipAuxpow(r); err != nil {
   105  			return nil, err
   106  		}
   107  	}
   108  
   109  	var w wire.MsgBlock
   110  	if err := utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w); err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	txs := make([]bchain.Tx, len(w.Transactions))
   115  	for ti, t := range w.Transactions {
   116  		txs[ti] = p.TxFromMsgTx(t, false)
   117  	}
   118  
   119  	return &bchain.Block{
   120  		BlockHeader: bchain.BlockHeader{
   121  			Size: len(b),
   122  			Time: h.Timestamp.Unix(),
   123  		},
   124  		Txs: txs,
   125  	}, nil
   126  }
   127  
   128  // ParseTxFromJson parses JSON message containing transaction and returns Tx struct
   129  func (p *DecredParser) ParseTxFromJson(jsonTx json.RawMessage) (*bchain.Tx, error) {
   130  	var getTxResult GetTransactionResult
   131  	if err := json.Unmarshal([]byte(jsonTx), &getTxResult.Result); err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	vins := make([]bchain.Vin, len(getTxResult.Result.Vin))
   136  	for index, input := range getTxResult.Result.Vin {
   137  		hexData := bchain.ScriptSig{}
   138  		if input.ScriptSig != nil {
   139  			hexData.Hex = input.ScriptSig.Hex
   140  		}
   141  
   142  		vins[index] = bchain.Vin{
   143  			Coinbase:  input.Coinbase,
   144  			Txid:      input.Txid,
   145  			Vout:      input.Vout,
   146  			ScriptSig: hexData,
   147  			Sequence:  input.Sequence,
   148  			// Addresses: []string{},
   149  		}
   150  	}
   151  
   152  	vouts := make([]bchain.Vout, len(getTxResult.Result.Vout))
   153  	for index, output := range getTxResult.Result.Vout {
   154  		addr := output.ScriptPubKey.Addresses
   155  		// If nulldata type found make asm field the address data.
   156  		if output.ScriptPubKey.Type == "nulldata" {
   157  			addr = []string{output.ScriptPubKey.Asm}
   158  		}
   159  
   160  		vouts[index] = bchain.Vout{
   161  			ValueSat: *big.NewInt(int64(math.Round(output.Value * 1e8))),
   162  			N:        output.N,
   163  			ScriptPubKey: bchain.ScriptPubKey{
   164  				Hex:       output.ScriptPubKey.Hex,
   165  				Addresses: addr,
   166  			},
   167  		}
   168  	}
   169  
   170  	tx := &bchain.Tx{
   171  		Hex:           getTxResult.Result.Hex,
   172  		Txid:          getTxResult.Result.Txid,
   173  		Version:       getTxResult.Result.Version,
   174  		LockTime:      getTxResult.Result.LockTime,
   175  		BlockHeight:   getTxResult.Result.BlockHeight,
   176  		Vin:           vins,
   177  		Vout:          vouts,
   178  		Confirmations: uint32(getTxResult.Result.Confirmations),
   179  		Time:          getTxResult.Result.Time,
   180  		Blocktime:     getTxResult.Result.Blocktime,
   181  	}
   182  
   183  	tx.CoinSpecificData = getTxResult.Result.TxExtraInfo
   184  
   185  	return tx, nil
   186  }
   187  
   188  // GetAddrDescForUnknownInput returns nil AddressDescriptor.
   189  func (p *DecredParser) GetAddrDescForUnknownInput(tx *bchain.Tx, input int) bchain.AddressDescriptor {
   190  	return nil
   191  }
   192  
   193  // GetAddrDescFromAddress returns internal address representation of a given address.
   194  func (p *DecredParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) {
   195  	addressByte := []byte(address)
   196  	return bchain.AddressDescriptor(addressByte), nil
   197  }
   198  
   199  // GetAddrDescFromVout returns internal address representation of a given transaction output.
   200  func (p *DecredParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) {
   201  	script, err := hex.DecodeString(output.ScriptPubKey.Hex)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  
   206  	scriptClass, addresses, _, err := txscript.ExtractPkScriptAddrs(txscript.DefaultScriptVersion, script, p.netConfig)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	if scriptClass.String() == "nulldata" {
   212  		if parsedOPReturn := p.BitcoinParser.TryParseOPReturn(script); parsedOPReturn != "" {
   213  			return []byte(parsedOPReturn), nil
   214  		}
   215  	}
   216  
   217  	var addressByte []byte
   218  	for i := range addresses {
   219  		addressByte = append(addressByte, addresses[i].String()...)
   220  	}
   221  	return bchain.AddressDescriptor(addressByte), nil
   222  }
   223  
   224  // GetAddressesFromAddrDesc returns addresses obtained from the internal address representation
   225  func (p *DecredParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) {
   226  	var addrs []string
   227  	if addrDesc != nil {
   228  		addrs = append(addrs, string(addrDesc))
   229  	}
   230  	return addrs, true, nil
   231  }
   232  
   233  // PackTx packs transaction to byte array using protobuf
   234  func (p *DecredParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
   235  	return p.baseParser.PackTx(tx, height, blockTime)
   236  }
   237  
   238  // UnpackTx unpacks transaction from protobuf byte array
   239  func (p *DecredParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
   240  	return p.baseParser.UnpackTx(buf)
   241  }
   242  
   243  func (p *DecredParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (bchain.AddressDescriptor, error) {
   244  	var addr, err = extKey.Address(p.netConfig)
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  	return p.GetAddrDescFromAddress(addr.String())
   249  }
   250  
   251  // DeriveAddressDescriptors derives address descriptors from given xpub for
   252  // listed indexes
   253  func (p *DecredParser) DeriveAddressDescriptors(xpub string, change uint32,
   254  	indexes []uint32) ([]bchain.AddressDescriptor, error) {
   255  	extKey, err := hdkeychain.NewKeyFromString(xpub)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  
   260  	changeExtKey, err := extKey.Child(change)
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  
   265  	ad := make([]bchain.AddressDescriptor, len(indexes))
   266  	for i, index := range indexes {
   267  		indexExtKey, err := changeExtKey.Child(index)
   268  		if err != nil {
   269  			return nil, err
   270  		}
   271  		ad[i], err = p.addrDescFromExtKey(indexExtKey)
   272  		if err != nil {
   273  			return nil, err
   274  		}
   275  	}
   276  	return ad, nil
   277  }
   278  
   279  // DeriveAddressDescriptorsFromTo derives address descriptors from given xpub for
   280  // addresses in index range
   281  func (p *DecredParser) DeriveAddressDescriptorsFromTo(xpub string, change uint32,
   282  	fromIndex uint32, toIndex uint32) ([]bchain.AddressDescriptor, error) {
   283  	if toIndex <= fromIndex {
   284  		return nil, errors.New("toIndex<=fromIndex")
   285  	}
   286  	extKey, err := hdkeychain.NewKeyFromString(xpub)
   287  	if err != nil {
   288  		return nil, err
   289  	}
   290  	changeExtKey, err := extKey.Child(change)
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  
   295  	ad := make([]bchain.AddressDescriptor, toIndex-fromIndex)
   296  	for index := fromIndex; index < toIndex; index++ {
   297  		indexExtKey, err := changeExtKey.Child(index)
   298  		if err != nil {
   299  			return nil, err
   300  		}
   301  		ad[index-fromIndex], err = p.addrDescFromExtKey(indexExtKey)
   302  		if err != nil {
   303  			return nil, err
   304  		}
   305  	}
   306  	return ad, nil
   307  }
   308  
   309  // DerivationBasePath returns base path of xpub which whose full format is
   310  // m/44'/<coin type>'/<account>'/<branch>/<address index>. This function only
   311  // returns a path up to m/44'/<coin type>'/<account>'/ whereby the rest of the
   312  // other details (<branch>/<address index>) are populated automatically.
   313  func (p *DecredParser) DerivationBasePath(xpub string) (string, error) {
   314  	var c string
   315  	cn, depth, err := p.decodeXpub(xpub)
   316  	if err != nil {
   317  		return "", err
   318  	}
   319  
   320  	if cn >= hdkeychain.HardenedKeyStart {
   321  		cn -= hdkeychain.HardenedKeyStart
   322  		c = "'"
   323  	}
   324  
   325  	c = strconv.Itoa(int(cn)) + c
   326  	if depth != 3 {
   327  		return "unknown/" + c, nil
   328  	}
   329  
   330  	return "m/44'/" + strconv.Itoa(int(p.Slip44)) + "'/" + c, nil
   331  }
   332  
   333  func (p *DecredParser) decodeXpub(xpub string) (childNum uint32, depth uint16, err error) {
   334  	decoded := base58.Decode(xpub)
   335  
   336  	// serializedKeyLen is the length of a serialized public or private
   337  	// extended key.  It consists of 4 bytes version, 1 byte depth, 4 bytes
   338  	// fingerprint, 4 bytes child number, 32 bytes chain code, and 33 bytes
   339  	// public/private key data.
   340  	serializedKeyLen := 4 + 1 + 4 + 4 + 32 + 33 // 78 bytes
   341  	if len(decoded) != serializedKeyLen+4 {
   342  		err = errors.New("invalid extended key length")
   343  		return
   344  	}
   345  
   346  	payload := decoded[:len(decoded)-4]
   347  	checkSum := decoded[len(decoded)-4:]
   348  	expectedCheckSum := chainhash.HashB(chainhash.HashB(payload))[:4]
   349  	if !bytes.Equal(checkSum, expectedCheckSum) {
   350  		err = errors.New("bad checksum value")
   351  		return
   352  	}
   353  
   354  	depth = uint16(payload[4:5][0])
   355  	childNum = binary.BigEndian.Uint32(payload[9:13])
   356  	return
   357  }