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 }