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

     1  package pivx
     2  
     3  import (
     4  	"blockbook/bchain"
     5  	"blockbook/bchain/coins/btc"
     6  	"blockbook/bchain/coins/utils"
     7  	"bytes"
     8  	"io"
     9  
    10  	"encoding/hex"
    11  	"encoding/json"
    12  
    13  	"math/big"
    14  
    15  	"github.com/martinboehm/btcd/blockchain"
    16  
    17  	"github.com/juju/errors"
    18  	"github.com/martinboehm/btcd/wire"
    19  	"github.com/martinboehm/btcutil/chaincfg"
    20  )
    21  
    22  // magic numbers
    23  const (
    24  	MainnetMagic wire.BitcoinNet = 0xe9fdc490
    25  	TestnetMagic wire.BitcoinNet = 0xba657645
    26  
    27  	// Zerocoin op codes
    28  	OP_ZEROCOINMINT  = 0xc1
    29  	OP_ZEROCOINSPEND = 0xc2
    30  )
    31  
    32  // chain parameters
    33  var (
    34  	MainNetParams chaincfg.Params
    35  	TestNetParams chaincfg.Params
    36  )
    37  
    38  func init() {
    39  	// PIVX mainnet Address encoding magics
    40  	MainNetParams = chaincfg.MainNetParams
    41  	MainNetParams.Net = MainnetMagic
    42  	MainNetParams.PubKeyHashAddrID = []byte{30} // starting with 'D'
    43  	MainNetParams.ScriptHashAddrID = []byte{13}
    44  	MainNetParams.PrivateKeyID = []byte{212}
    45  
    46  	// PIVX testnet Address encoding magics
    47  	TestNetParams = chaincfg.TestNet3Params
    48  	TestNetParams.Net = TestnetMagic
    49  	TestNetParams.PubKeyHashAddrID = []byte{139} // starting with 'x' or 'y'
    50  	TestNetParams.ScriptHashAddrID = []byte{19}
    51  	TestNetParams.PrivateKeyID = []byte{239}
    52  }
    53  
    54  // PivXParser handle
    55  type PivXParser struct {
    56  	*btc.BitcoinParser
    57  	baseparser                         *bchain.BaseParser
    58  	BitcoinOutputScriptToAddressesFunc btc.OutputScriptToAddressesFunc
    59  }
    60  
    61  // NewPivXParser returns new PivXParser instance
    62  func NewPivXParser(params *chaincfg.Params, c *btc.Configuration) *PivXParser {
    63  	p := &PivXParser{
    64  		BitcoinParser: btc.NewBitcoinParser(params, c),
    65  		baseparser:    &bchain.BaseParser{},
    66  	}
    67  	p.BitcoinOutputScriptToAddressesFunc = p.OutputScriptToAddressesFunc
    68  	p.OutputScriptToAddressesFunc = p.outputScriptToAddresses
    69  	return p
    70  }
    71  
    72  // GetChainParams contains network parameters for the main PivX network
    73  func GetChainParams(chain string) *chaincfg.Params {
    74  	if !chaincfg.IsRegistered(&MainNetParams) {
    75  		err := chaincfg.Register(&MainNetParams)
    76  		if err == nil {
    77  			err = chaincfg.Register(&TestNetParams)
    78  		}
    79  		if err != nil {
    80  			panic(err)
    81  		}
    82  	}
    83  	switch chain {
    84  	case "test":
    85  		return &TestNetParams
    86  	default:
    87  		return &MainNetParams
    88  	}
    89  }
    90  
    91  // ParseBlock parses raw block to our Block struct
    92  func (p *PivXParser) ParseBlock(b []byte) (*bchain.Block, error) {
    93  	r := bytes.NewReader(b)
    94  	w := wire.MsgBlock{}
    95  	h := wire.BlockHeader{}
    96  	err := h.Deserialize(r)
    97  	if err != nil {
    98  		return nil, errors.Annotatef(err, "Deserialize")
    99  	}
   100  
   101  	if h.Version > 3 && h.Version < 7 {
   102  		// Skip past AccumulatorCheckpoint (block version 4, 5 and 6)
   103  		r.Seek(32, io.SeekCurrent)
   104  	}
   105  
   106  	err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w)
   107  	if err != nil {
   108  		return nil, errors.Annotatef(err, "DecodeTransactions")
   109  	}
   110  
   111  	txs := make([]bchain.Tx, len(w.Transactions))
   112  	for ti, t := range w.Transactions {
   113  		txs[ti] = p.TxFromMsgTx(t, false)
   114  	}
   115  
   116  	return &bchain.Block{
   117  		BlockHeader: bchain.BlockHeader{
   118  			Size: len(b),
   119  			Time: h.Timestamp.Unix(),
   120  		},
   121  		Txs: txs,
   122  	}, nil
   123  }
   124  
   125  // PackTx packs transaction to byte array using protobuf
   126  func (p *PivXParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
   127  	return p.baseparser.PackTx(tx, height, blockTime)
   128  }
   129  
   130  // UnpackTx unpacks transaction from protobuf byte array
   131  func (p *PivXParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
   132  	return p.baseparser.UnpackTx(buf)
   133  }
   134  
   135  // ParseTx parses byte array containing transaction and returns Tx struct
   136  func (p *PivXParser) ParseTx(b []byte) (*bchain.Tx, error) {
   137  	t := wire.MsgTx{}
   138  	r := bytes.NewReader(b)
   139  	if err := t.Deserialize(r); err != nil {
   140  		return nil, err
   141  	}
   142  	tx := p.TxFromMsgTx(&t, true)
   143  	tx.Hex = hex.EncodeToString(b)
   144  	return &tx, nil
   145  }
   146  
   147  // TxFromMsgTx parses tx and adds handling for OP_ZEROCOINSPEND inputs
   148  func (p *PivXParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx {
   149  	vin := make([]bchain.Vin, len(t.TxIn))
   150  	for i, in := range t.TxIn {
   151  
   152  		// extra check to not confuse Tx with single OP_ZEROCOINSPEND input as a coinbase Tx
   153  		if !isZeroCoinSpendScript(in.SignatureScript) && blockchain.IsCoinBaseTx(t) {
   154  			vin[i] = bchain.Vin{
   155  				Coinbase: hex.EncodeToString(in.SignatureScript),
   156  				Sequence: in.Sequence,
   157  			}
   158  			break
   159  		}
   160  
   161  		s := bchain.ScriptSig{
   162  			Hex: hex.EncodeToString(in.SignatureScript),
   163  			// missing: Asm,
   164  		}
   165  
   166  		txid := in.PreviousOutPoint.Hash.String()
   167  
   168  		vin[i] = bchain.Vin{
   169  			Txid:      txid,
   170  			Vout:      in.PreviousOutPoint.Index,
   171  			Sequence:  in.Sequence,
   172  			ScriptSig: s,
   173  		}
   174  	}
   175  	vout := make([]bchain.Vout, len(t.TxOut))
   176  	for i, out := range t.TxOut {
   177  		addrs := []string{}
   178  		if parseAddresses {
   179  			addrs, _, _ = p.OutputScriptToAddressesFunc(out.PkScript)
   180  		}
   181  		s := bchain.ScriptPubKey{
   182  			Hex:       hex.EncodeToString(out.PkScript),
   183  			Addresses: addrs,
   184  			// missing: Asm,
   185  			// missing: Type,
   186  		}
   187  		var vs big.Int
   188  		vs.SetInt64(out.Value)
   189  		vout[i] = bchain.Vout{
   190  			ValueSat:     vs,
   191  			N:            uint32(i),
   192  			ScriptPubKey: s,
   193  		}
   194  	}
   195  	tx := bchain.Tx{
   196  		Txid:     t.TxHash().String(),
   197  		Version:  t.Version,
   198  		LockTime: t.LockTime,
   199  		Vin:      vin,
   200  		Vout:     vout,
   201  		// skip: BlockHash,
   202  		// skip: Confirmations,
   203  		// skip: Time,
   204  		// skip: Blocktime,
   205  	}
   206  	return tx
   207  }
   208  
   209  // ParseTxFromJson parses JSON message containing transaction and returns Tx struct
   210  func (p *PivXParser) ParseTxFromJson(msg json.RawMessage) (*bchain.Tx, error) {
   211  	var tx bchain.Tx
   212  	err := json.Unmarshal(msg, &tx)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	for i := range tx.Vout {
   218  		vout := &tx.Vout[i]
   219  		// convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal
   220  		vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue)
   221  		if err != nil {
   222  			return nil, err
   223  		}
   224  		vout.JsonValue = ""
   225  
   226  		if vout.ScriptPubKey.Addresses == nil {
   227  			vout.ScriptPubKey.Addresses = []string{}
   228  		}
   229  	}
   230  
   231  	return &tx, nil
   232  }
   233  
   234  // outputScriptToAddresses converts ScriptPubKey to bitcoin addresses
   235  func (p *PivXParser) outputScriptToAddresses(script []byte) ([]string, bool, error) {
   236  	if isZeroCoinSpendScript(script) {
   237  		return []string{"Zerocoin Spend"}, false, nil
   238  	}
   239  	if isZeroCoinMintScript(script) {
   240  		return []string{"Zerocoin Mint"}, false, nil
   241  	}
   242  
   243  	rv, s, _ := p.BitcoinOutputScriptToAddressesFunc(script)
   244  	return rv, s, nil
   245  }
   246  
   247  func (p *PivXParser) GetAddrDescForUnknownInput(tx *bchain.Tx, input int) bchain.AddressDescriptor {
   248  	if len(tx.Vin) > input {
   249  		scriptHex := tx.Vin[input].ScriptSig.Hex
   250  
   251  		if scriptHex != "" {
   252  			script, _ := hex.DecodeString(scriptHex)
   253  			return script
   254  		}
   255  	}
   256  
   257  	s := make([]byte, 10)
   258  	return s
   259  }
   260  
   261  // Checks if script is OP_ZEROCOINMINT
   262  func isZeroCoinMintScript(signatureScript []byte) bool {
   263  	return len(signatureScript) > 1 && signatureScript[0] == OP_ZEROCOINMINT
   264  }
   265  
   266  // Checks if script is OP_ZEROCOINSPEND
   267  func isZeroCoinSpendScript(signatureScript []byte) bool {
   268  	return len(signatureScript) >= 100 && signatureScript[0] == OP_ZEROCOINSPEND
   269  }