package divi import ( "bytes" "encoding/hex" "encoding/json" "io" "math/big" "github.com/juju/errors" "github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcutil/chaincfg" "spacecruft.org/spacecruft/blockbook/bchain" "spacecruft.org/spacecruft/blockbook/bchain/coins/btc" "spacecruft.org/spacecruft/blockbook/bchain/coins/utils" ) const ( // MainnetMagic = "network messages so the messages can be identified to belong to a specific coin" // Source https://github.com/DiviProject/Divi/blob/master0/divi/src/chainparams.cpp#L128-L136 MainnetMagic wire.BitcoinNet = 0x8f8da0df ) var ( // MainNetParams = ??? MainNetParams chaincfg.Params ) func init() { // DIVI mainnet Address encoding magics MainNetParams = chaincfg.MainNetParams MainNetParams.Net = MainnetMagic MainNetParams.PubKeyHashAddrID = []byte{30} // starting with 'D' MainNetParams.ScriptHashAddrID = []byte{13} MainNetParams.PrivateKeyID = []byte{212} } // DivicoinParser handle type DivicoinParser struct { *btc.BitcoinParser baseparser *bchain.BaseParser BitcoinOutputScriptToAddressesFunc btc.OutputScriptToAddressesFunc } // NewDiviParser returns new DivicoinParser instance func NewDiviParser(params *chaincfg.Params, c *btc.Configuration) *DivicoinParser { p := &DivicoinParser{ BitcoinParser: btc.NewBitcoinParser(params, c), baseparser: &bchain.BaseParser{}, } p.BitcoinOutputScriptToAddressesFunc = p.OutputScriptToAddressesFunc p.OutputScriptToAddressesFunc = p.outputScriptToAddresses return p } // GetChainParams contains network parameters for the main Divi network func GetChainParams(chain string) *chaincfg.Params { if !chaincfg.IsRegistered(&MainNetParams) { err := chaincfg.Register(&MainNetParams) /*if err == nil { err = chaincfg.Register(&TestNetParams) }*/ if err != nil { panic(err) } } /* switch chain { case "test": return &TestNetParams default: */return &MainNetParams //} } // ParseBlock parses raw block to our Block struct func (p *DivicoinParser) ParseBlock(b []byte) (*bchain.Block, error) { r := bytes.NewReader(b) w := wire.MsgBlock{} h := wire.BlockHeader{} err := h.Deserialize(r) if err != nil { return nil, errors.Annotatef(err, "Deserialize") } if h.Version > 3 { // Skip past AccumulatorCheckpoint which was added in pivx block version 4 r.Seek(32, io.SeekCurrent) } err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w) if err != nil { return nil, errors.Annotatef(err, "DecodeTransactions") } txs := make([]bchain.Tx, len(w.Transactions)) for ti, t := range w.Transactions { txs[ti] = p.TxFromMsgTx(t, false) } return &bchain.Block{ BlockHeader: bchain.BlockHeader{ Size: len(b), Time: h.Timestamp.Unix(), }, Txs: txs, }, nil } // PackTx packs transaction to byte array using protobuf func (p *DivicoinParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { return p.baseparser.PackTx(tx, height, blockTime) } // UnpackTx unpacks transaction from protobuf byte array func (p *DivicoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { return p.baseparser.UnpackTx(buf) } // ParseTx parses byte array containing transaction and returns Tx struct func (p *DivicoinParser) ParseTx(b []byte) (*bchain.Tx, error) { t := wire.MsgTx{} r := bytes.NewReader(b) if err := t.Deserialize(r); err != nil { return nil, err } tx := p.TxFromMsgTx(&t, true) tx.Hex = hex.EncodeToString(b) return &tx, nil } // TxFromMsgTx parses tx and adds handling for OP_ZEROCOINSPEND inputs func (p *DivicoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx { vin := make([]bchain.Vin, len(t.TxIn)) for i, in := range t.TxIn { s := bchain.ScriptSig{ Hex: hex.EncodeToString(in.SignatureScript), // missing: Asm, } txid := in.PreviousOutPoint.Hash.String() vin[i] = bchain.Vin{ Txid: txid, Vout: in.PreviousOutPoint.Index, Sequence: in.Sequence, ScriptSig: s, } } vout := make([]bchain.Vout, len(t.TxOut)) for i, out := range t.TxOut { addrs := []string{} if parseAddresses { addrs, _, _ = p.OutputScriptToAddressesFunc(out.PkScript) } s := bchain.ScriptPubKey{ Hex: hex.EncodeToString(out.PkScript), Addresses: addrs, // missing: Asm, // missing: Type, } var vs big.Int vs.SetInt64(out.Value) vout[i] = bchain.Vout{ ValueSat: vs, N: uint32(i), ScriptPubKey: s, } } tx := bchain.Tx{ Txid: t.TxHash().String(), Version: t.Version, LockTime: t.LockTime, Vin: vin, Vout: vout, // skip: BlockHash, // skip: Confirmations, // skip: Time, // skip: Blocktime, } return tx } // ParseTxFromJSON parses JSON message containing transaction and returns Tx struct func (p *DivicoinParser) ParseTxFromJSON(msg json.RawMessage) (*bchain.Tx, error) { var tx bchain.Tx err := json.Unmarshal(msg, &tx) if err != nil { return nil, err } for i := range tx.Vout { vout := &tx.Vout[i] // convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue) if err != nil { return nil, err } vout.JsonValue = "" if vout.ScriptPubKey.Addresses == nil { vout.ScriptPubKey.Addresses = []string{} } } return &tx, nil } // outputScriptToAddresses converts ScriptPubKey to bitcoin addresses func (p *DivicoinParser) outputScriptToAddresses(script []byte) ([]string, bool, error) { rv, s, _ := p.BitcoinOutputScriptToAddressesFunc(script) return rv, s, nil } // GetAddrDescForUnknownInput = ??? func (p *DivicoinParser) GetAddrDescForUnknownInput(tx *bchain.Tx, input int) bchain.AddressDescriptor { if len(tx.Vin) > input { scriptHex := tx.Vin[input].ScriptSig.Hex if scriptHex != "" { script, _ := hex.DecodeString(scriptHex) return script } } s := make([]byte, 10) return s }