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