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

     1  package xzc
     2  
     3  import (
     4  	"blockbook/bchain"
     5  	"blockbook/bchain/coins/btc"
     6  	"bytes"
     7  	"encoding/binary"
     8  	"encoding/json"
     9  	"io"
    10  
    11  	"github.com/martinboehm/btcd/chaincfg/chainhash"
    12  	"github.com/martinboehm/btcd/wire"
    13  	"github.com/martinboehm/btcutil/chaincfg"
    14  )
    15  
    16  const (
    17  	OpZeroCoinMint  = 0xc1
    18  	OpZeroCoinSpend = 0xc2
    19  	OpSigmaMint     = 0xc3
    20  	OpSigmaSpend    = 0xc4
    21  
    22  	MainnetMagic wire.BitcoinNet = 0xe3d9fef1
    23  	TestnetMagic wire.BitcoinNet = 0xcffcbeea
    24  	RegtestMagic wire.BitcoinNet = 0xfabfb5da
    25  
    26  	GenesisBlockTime       = 1414776286
    27  	SwitchToMTPBlockHeader = 1544443200
    28  	MTPL                   = 64
    29  
    30  	SpendTxID = "0000000000000000000000000000000000000000000000000000000000000000"
    31  )
    32  
    33  var (
    34  	MainNetParams chaincfg.Params
    35  	TestNetParams chaincfg.Params
    36  	RegtestParams chaincfg.Params
    37  )
    38  
    39  func init() {
    40  	// mainnet
    41  	MainNetParams = chaincfg.MainNetParams
    42  	MainNetParams.Net = MainnetMagic
    43  
    44  	MainNetParams.AddressMagicLen = 1
    45  	MainNetParams.PubKeyHashAddrID = []byte{0x52}
    46  	MainNetParams.ScriptHashAddrID = []byte{0x07}
    47  
    48  	// testnet
    49  	TestNetParams = chaincfg.TestNet3Params
    50  	TestNetParams.Net = TestnetMagic
    51  
    52  	TestNetParams.AddressMagicLen = 1
    53  	TestNetParams.PubKeyHashAddrID = []byte{0x41}
    54  	TestNetParams.ScriptHashAddrID = []byte{0xb2}
    55  
    56  	// regtest
    57  	RegtestParams = chaincfg.RegressionNetParams
    58  	RegtestParams.Net = RegtestMagic
    59  }
    60  
    61  // ZcoinParser handle
    62  type ZcoinParser struct {
    63  	*btc.BitcoinParser
    64  }
    65  
    66  // NewZcoinParser returns new ZcoinParser instance
    67  func NewZcoinParser(params *chaincfg.Params, c *btc.Configuration) *ZcoinParser {
    68  	return &ZcoinParser{
    69  		BitcoinParser: btc.NewBitcoinParser(params, c),
    70  	}
    71  }
    72  
    73  // GetChainParams contains network parameters for the main Zcoin network,
    74  // the regression test Zcoin network, the test Zcoin network and
    75  // the simulation test Zcoin network, in this order
    76  func GetChainParams(chain string) *chaincfg.Params {
    77  	if !chaincfg.IsRegistered(&MainNetParams) {
    78  		err := chaincfg.Register(&MainNetParams)
    79  		if err == nil {
    80  			err = chaincfg.Register(&TestNetParams)
    81  		}
    82  		if err == nil {
    83  			err = chaincfg.Register(&RegtestParams)
    84  		}
    85  		if err != nil {
    86  			panic(err)
    87  		}
    88  	}
    89  	switch chain {
    90  	case "test":
    91  		return &TestNetParams
    92  	case "regtest":
    93  		return &RegtestParams
    94  	default:
    95  		return &MainNetParams
    96  	}
    97  }
    98  
    99  // GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable
   100  func (p *ZcoinParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) {
   101  
   102  	if len(addrDesc) > 0 {
   103  		switch addrDesc[0] {
   104  		case OpZeroCoinMint:
   105  			return []string{"Zeromint"}, false, nil
   106  		case OpZeroCoinSpend:
   107  			return []string{"Zerospend"}, false, nil
   108  		case OpSigmaMint:
   109  			return []string{"Sigmamint"}, false, nil
   110  		case OpSigmaSpend:
   111  			return []string{"Sigmaspend"}, false, nil
   112  		}
   113  	}
   114  
   115  	return p.OutputScriptToAddressesFunc(addrDesc)
   116  }
   117  
   118  // PackTx packs transaction to byte array using protobuf
   119  func (p *ZcoinParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
   120  	return p.BaseParser.PackTx(tx, height, blockTime)
   121  }
   122  
   123  // UnpackTx unpacks transaction from protobuf byte array
   124  func (p *ZcoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
   125  	return p.BaseParser.UnpackTx(buf)
   126  }
   127  
   128  // ParseBlock parses raw block to our Block struct
   129  func (p *ZcoinParser) ParseBlock(b []byte) (*bchain.Block, error) {
   130  	reader := bytes.NewReader(b)
   131  
   132  	// parse standard block header first
   133  	header, err := parseBlockHeader(reader)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	// then MTP header
   139  	if isMTP(header) {
   140  		mtpHeader := MTPBlockHeader{}
   141  		mtpHashData := MTPHashData{}
   142  
   143  		// header
   144  		err = binary.Read(reader, binary.LittleEndian, &mtpHeader)
   145  		if err != nil {
   146  			return nil, err
   147  		}
   148  
   149  		// hash data
   150  		err = binary.Read(reader, binary.LittleEndian, &mtpHashData)
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  
   155  		// proof
   156  		for i := 0; i < MTPL*3; i++ {
   157  			var numberProofBlocks uint8
   158  
   159  			err = binary.Read(reader, binary.LittleEndian, &numberProofBlocks)
   160  			if err != nil {
   161  				return nil, err
   162  			}
   163  
   164  			for j := uint8(0); j < numberProofBlocks; j++ {
   165  				var mtpData [16]uint8
   166  
   167  				err = binary.Read(reader, binary.LittleEndian, mtpData[:])
   168  				if err != nil {
   169  					return nil, err
   170  				}
   171  			}
   172  		}
   173  	}
   174  
   175  	// parse txs
   176  	ntx, err := wire.ReadVarInt(reader, 0)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  
   181  	txs := make([]bchain.Tx, ntx)
   182  
   183  	for i := uint64(0); i < ntx; i++ {
   184  		tx := wire.MsgTx{}
   185  
   186  		err := tx.BtcDecode(reader, 0, wire.WitnessEncoding)
   187  		if err != nil {
   188  			return nil, err
   189  		}
   190  
   191  		btx := p.TxFromMsgTx(&tx, false)
   192  
   193  		p.parseZcoinTx(&btx)
   194  
   195  		txs[i] = btx
   196  	}
   197  
   198  	return &bchain.Block{
   199  		BlockHeader: bchain.BlockHeader{
   200  			Size: len(b),
   201  			Time: header.Timestamp.Unix(),
   202  		},
   203  		Txs: txs,
   204  	}, nil
   205  }
   206  
   207  // ParseTxFromJson parses JSON message containing transaction and returns Tx struct
   208  func (p *ZcoinParser) ParseTxFromJson(msg json.RawMessage) (*bchain.Tx, error) {
   209  	var tx bchain.Tx
   210  	err := json.Unmarshal(msg, &tx)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  
   215  	for i := range tx.Vout {
   216  		vout := &tx.Vout[i]
   217  		// convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal
   218  		vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue)
   219  		if err != nil {
   220  			return nil, err
   221  		}
   222  		vout.JsonValue = ""
   223  	}
   224  
   225  	p.parseZcoinTx(&tx)
   226  
   227  	return &tx, nil
   228  }
   229  
   230  func (p *ZcoinParser) parseZcoinTx(tx *bchain.Tx) error {
   231  	for i := range tx.Vin {
   232  		vin := &tx.Vin[i]
   233  
   234  		// FIXME: right now we treat zerocoin spend vin as coinbase
   235  		// change this after blockbook support special type of vin
   236  		if vin.Txid == SpendTxID {
   237  			vin.Coinbase = vin.Txid
   238  			vin.Txid = ""
   239  			vin.Sequence = 0
   240  			vin.Vout = 0
   241  		}
   242  	}
   243  
   244  	return nil
   245  }
   246  
   247  func parseBlockHeader(r io.Reader) (*wire.BlockHeader, error) {
   248  	h := &wire.BlockHeader{}
   249  	err := h.Deserialize(r)
   250  	return h, err
   251  }
   252  
   253  func isMTP(h *wire.BlockHeader) bool {
   254  	epoch := h.Timestamp.Unix()
   255  
   256  	// the genesis block never be MTP block
   257  	return epoch > GenesisBlockTime && epoch >= SwitchToMTPBlockHeader
   258  }
   259  
   260  type MTPHashData struct {
   261  	HashRootMTP [16]uint8
   262  	BlockMTP    [128][128]uint64
   263  }
   264  
   265  type MTPBlockHeader struct {
   266  	VersionMTP   int32
   267  	MTPHashValue chainhash.Hash
   268  	Reserved1    chainhash.Hash
   269  	Reserved2    chainhash.Hash
   270  }