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 }