github.com/trezor/blockbook@v0.4.1-0.20240328132726-e9a08582ee2c/bchain/coins/omotenashicoin/omotenashicoinparser.go (about)

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