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 }