github.com/cryptohub-digital/blockbook-fork@v0.0.0-20230713133354-673c927af7f1/bchain/coins/xcb/xcbparser.go (about) 1 package xcb 2 3 import ( 4 "encoding/hex" 5 "math/big" 6 "strconv" 7 8 xcbcommon "github.com/core-coin/go-core/v2/common" 9 "github.com/core-coin/go-core/v2/common/hexutil" 10 "github.com/cryptohub-digital/blockbook-fork/bchain" 11 "github.com/golang/protobuf/proto" 12 "github.com/juju/errors" 13 ) 14 15 // CoreCoinTypeAddressDescriptorLen - the AddressDescriptor of Core Coin has fixed length 16 const CoreCoinTypeAddressDescriptorLen = 22 17 18 // CoreCoinTypeTxidLen - the length of Txid 19 const CoreCoinTypeTxidLen = 32 20 21 // CoreAmountDecimalPoint defines number of decimal points in Core amounts 22 const CoreAmountDecimalPoint = 18 23 24 // CoreCoinParser handle 25 type CoreCoinParser struct { 26 *bchain.BaseParser 27 } 28 29 // NewCoreCoinParser returns new CoreCoinParser instance 30 func NewCoreCoinParser(b int) *CoreCoinParser { 31 return &CoreCoinParser{&bchain.BaseParser{ 32 BlockAddressesToKeep: b, 33 AmountDecimalPoint: CoreAmountDecimalPoint, 34 }} 35 } 36 37 type rpcHeader struct { 38 Hash string `json:"hash"` 39 ParentHash string `json:"parentHash"` 40 Difficulty string `json:"difficulty"` 41 Number string `json:"number"` 42 Time string `json:"timestamp"` 43 Size string `json:"size"` 44 Nonce string `json:"nonce"` 45 } 46 47 type rpcLogWithTxHash struct { 48 RpcLog 49 Hash string `json:"transactionHash"` 50 } 51 52 type rpcBlockTransactions struct { 53 Transactions []RpcTransaction `json:"transactions"` 54 } 55 56 type rpcBlockTxids struct { 57 Transactions []string `json:"transactions"` 58 } 59 60 func xcbNumber(n string) (int64, error) { 61 if len(n) > 2 { 62 return strconv.ParseInt(n[2:], 16, 64) 63 } 64 return 0, errors.Errorf("Not a number: '%v'", n) 65 } 66 67 func (p *CoreCoinParser) xcbTxToTx(tx *RpcTransaction, receipt *RpcReceipt, blocktime int64, confirmations uint32) (*bchain.Tx, error) { 68 txid := tx.Hash 69 var ( 70 fa, ta []string 71 err error 72 ) 73 if len(tx.From) > 2 { 74 fa = []string{tx.From} 75 } 76 if len(tx.To) > 2 { 77 ta = []string{tx.To} 78 } 79 ct := CoreCoinSpecificData{ 80 Tx: tx, 81 Receipt: receipt, 82 } 83 vs, err := hexutil.DecodeBig(tx.Value) 84 if err != nil { 85 return nil, err 86 } 87 return &bchain.Tx{ 88 Blocktime: blocktime, 89 Confirmations: confirmations, 90 Time: blocktime, 91 Txid: txid, 92 Vin: []bchain.Vin{ 93 { 94 Addresses: fa, 95 }, 96 }, 97 Vout: []bchain.Vout{ 98 { 99 N: 0, // there is always up to one To address 100 ValueSat: *vs, 101 ScriptPubKey: bchain.ScriptPubKey{ 102 Addresses: ta, 103 }, 104 }, 105 }, 106 CoinSpecificData: ct, 107 }, nil 108 } 109 110 // GetAddrDescFromVout returns internal address representation of given transaction output 111 func (p *CoreCoinParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) { 112 if len(output.ScriptPubKey.Addresses) != 1 { 113 return nil, bchain.ErrAddressMissing 114 } 115 return p.GetAddrDescFromAddress(output.ScriptPubKey.Addresses[0]) 116 } 117 118 func has0xPrefix(s string) bool { 119 return len(s) >= 2 && s[0] == '0' && (s[1]|32) == 'x' 120 } 121 122 // GetAddrDescFromAddress returns internal address representation of given address 123 func (p *CoreCoinParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) { 124 if address == "" { 125 return nil, bchain.ErrAddressMissing 126 } 127 parsed, err := xcbcommon.HexToAddress(address) 128 if err != nil { 129 return nil, err 130 } 131 return parsed.Bytes(), nil 132 } 133 134 // GetAddressesFromAddrDesc returns addresse for given address descriptor 135 func (p *CoreCoinParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) { 136 addr, err := xcbcommon.HexToAddress(xcbcommon.Bytes2Hex(addrDesc)) 137 if err != nil { 138 return []string{}, false, err 139 } 140 return []string{addr.Hex()}, true, nil 141 } 142 143 // GetScriptFromAddrDesc returns output script for given address descriptor 144 func (p *CoreCoinParser) GetScriptFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]byte, error) { 145 return addrDesc, nil 146 } 147 148 func hexDecode(s string) ([]byte, error) { 149 b, err := hexutil.Decode(s) 150 if err != nil && err != hexutil.ErrEmptyString { 151 return nil, err 152 } 153 return b, nil 154 } 155 156 func hexDecodeBig(s string) ([]byte, error) { 157 b, err := hexutil.DecodeBig(s) 158 if err != nil { 159 return nil, err 160 } 161 return b.Bytes(), nil 162 } 163 164 func hexEncodeBig(b []byte) string { 165 var i big.Int 166 i.SetBytes(b) 167 return hexutil.EncodeBig(&i) 168 } 169 170 func decodeAddress(addr []byte) (string, error) { 171 address, err := xcbcommon.HexToAddress(xcbcommon.Bytes2Hex(addr)) 172 if err != nil { 173 return "", err 174 } 175 return address.Hex(), nil 176 } 177 178 // PackTx packs transaction to byte array 179 // completeTransaction.InternalData are not packed, they are stored in a different table 180 func (p *CoreCoinParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { 181 var err error 182 var n uint64 183 r, ok := tx.CoinSpecificData.(CoreCoinSpecificData) 184 if !ok { 185 return nil, errors.New("Missing CoinSpecificData") 186 } 187 pt := &ProtoXCBCompleteTransaction{} 188 pt.Tx = &ProtoXCBCompleteTransaction_TxType{} 189 if pt.Tx.AccountNonce, err = hexutil.DecodeUint64(r.Tx.AccountNonce); err != nil { 190 return nil, errors.Annotatef(err, "AccountNonce %v", r.Tx.AccountNonce) 191 } 192 // pt.BlockNumber = height 193 if n, err = hexutil.DecodeUint64(r.Tx.BlockNumber); err != nil { 194 return nil, errors.Annotatef(err, "BlockNumber %v", r.Tx.BlockNumber) 195 } 196 pt.BlockNumber = uint32(n) 197 pt.BlockTime = uint64(blockTime) 198 if pt.Tx.From, err = hexDecode(r.Tx.From); err != nil { 199 return nil, errors.Annotatef(err, "From %v", r.Tx.From) 200 } 201 if pt.Tx.EnergyLimit, err = hexutil.DecodeUint64(r.Tx.EnergyLimit); err != nil { 202 return nil, errors.Annotatef(err, "EnergyLimit %v", r.Tx.EnergyLimit) 203 } 204 if pt.Tx.Hash, err = hexDecode(r.Tx.Hash); err != nil { 205 return nil, errors.Annotatef(err, "Hash %v", r.Tx.Hash) 206 } 207 if pt.Tx.Payload, err = hexDecode(r.Tx.Payload); err != nil { 208 return nil, errors.Annotatef(err, "Payload %v", r.Tx.Payload) 209 } 210 if pt.Tx.EnergyPrice, err = hexDecodeBig(r.Tx.EnergyPrice); err != nil { 211 return nil, errors.Annotatef(err, "EnergyPrice %v", r.Tx.EnergyPrice) 212 } 213 if pt.Tx.To, err = hexDecode(r.Tx.To); err != nil { 214 return nil, errors.Annotatef(err, "To %v", r.Tx.To) 215 } 216 if n, err = hexutil.DecodeUint64(r.Tx.TransactionIndex); err != nil { 217 return nil, errors.Annotatef(err, "TransactionIndex %v", r.Tx.TransactionIndex) 218 } 219 pt.Tx.TransactionIndex = uint32(n) 220 if pt.Tx.Value, err = hexDecodeBig(r.Tx.Value); err != nil { 221 return nil, errors.Annotatef(err, "Value %v", r.Tx.Value) 222 } 223 if r.Receipt != nil { 224 pt.Receipt = &ProtoXCBCompleteTransaction_ReceiptType{} 225 if pt.Receipt.EnergyUsed, err = hexDecodeBig(r.Receipt.EnergyUsed); err != nil { 226 return nil, errors.Annotatef(err, "EnergyUsed %v", r.Receipt.EnergyUsed) 227 } 228 if r.Receipt.Status != "" { 229 if pt.Receipt.Status, err = hexDecodeBig(r.Receipt.Status); err != nil { 230 return nil, errors.Annotatef(err, "Status %v", r.Receipt.Status) 231 } 232 } else { 233 // unknown status, use 'U' as status bytes 234 // there is a potential for conflict with value 0x55 but this is not used by any chain at this moment 235 pt.Receipt.Status = []byte{'U'} 236 } 237 ptLogs := make([]*ProtoXCBCompleteTransaction_ReceiptType_LogType, len(r.Receipt.Logs)) 238 for i, l := range r.Receipt.Logs { 239 a, err := hexutil.Decode(l.Address) 240 if err != nil { 241 return nil, errors.Annotatef(err, "Address cannot be decoded %v", l) 242 } 243 d, err := hexutil.Decode(l.Data) 244 if err != nil { 245 return nil, errors.Annotatef(err, "Data cannot be decoded %v", l) 246 } 247 t := make([][]byte, len(l.Topics)) 248 for j, s := range l.Topics { 249 t[j], err = hexutil.Decode(s) 250 if err != nil { 251 return nil, errors.Annotatef(err, "Topic cannot be decoded %v", l) 252 } 253 } 254 ptLogs[i] = &ProtoXCBCompleteTransaction_ReceiptType_LogType{ 255 Address: a, 256 Data: d, 257 Topics: t, 258 } 259 260 } 261 pt.Receipt.Log = ptLogs 262 } 263 return proto.Marshal(pt) 264 } 265 266 // UnpackTx unpacks transaction from byte array 267 func (p *CoreCoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { 268 var pt ProtoXCBCompleteTransaction 269 err := proto.Unmarshal(buf, &pt) 270 if err != nil { 271 return nil, 0, err 272 } 273 from, err := decodeAddress(pt.Tx.From) 274 if err != nil { 275 return nil, 0, err 276 } 277 278 to, err := decodeAddress(pt.Tx.To) 279 if err != nil { 280 return nil, 0, err 281 } 282 rt := RpcTransaction{ 283 AccountNonce: hexutil.EncodeUint64(pt.Tx.AccountNonce), 284 BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)), 285 From: from, 286 EnergyLimit: hexutil.EncodeUint64(pt.Tx.EnergyLimit), 287 Hash: hexutil.Encode(pt.Tx.Hash), 288 Payload: hexutil.Encode(pt.Tx.Payload), 289 EnergyPrice: hexEncodeBig(pt.Tx.EnergyPrice), 290 To: to, 291 TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)), 292 Value: hexEncodeBig(pt.Tx.Value), 293 } 294 var rr *RpcReceipt 295 if pt.Receipt != nil { 296 logs := make([]*RpcLog, len(pt.Receipt.Log)) 297 for i, l := range pt.Receipt.Log { 298 topics := make([]string, len(l.Topics)) 299 for j, t := range l.Topics { 300 topics[j] = hexutil.Encode(t) 301 } 302 address, err := decodeAddress(l.Address) 303 if err != nil { 304 return nil, 0, err 305 } 306 logs[i] = &RpcLog{ 307 Address: address, 308 Data: hexutil.Encode(l.Data), 309 Topics: topics, 310 } 311 } 312 status := "" 313 // handle a special value []byte{'U'} as unknown state 314 if len(pt.Receipt.Status) != 1 || pt.Receipt.Status[0] != 'U' { 315 status = hexEncodeBig(pt.Receipt.Status) 316 } 317 rr = &RpcReceipt{ 318 EnergyUsed: hexEncodeBig(pt.Receipt.EnergyUsed), 319 Status: status, 320 Logs: logs, 321 } 322 } 323 tx, err := p.xcbTxToTx(&rt, rr, int64(pt.BlockTime), 0) 324 if err != nil { 325 return nil, 0, err 326 } 327 return tx, pt.BlockNumber, nil 328 } 329 330 // PackedTxidLen returns length in bytes of packed txid 331 func (p *CoreCoinParser) PackedTxidLen() int { 332 return CoreCoinTypeTxidLen 333 } 334 335 // PackTxid packs txid to byte array 336 func (p *CoreCoinParser) PackTxid(txid string) ([]byte, error) { 337 if has0xPrefix(txid) { 338 txid = txid[2:] 339 } 340 return hex.DecodeString(txid) 341 } 342 343 // UnpackTxid unpacks byte array to txid 344 func (p *CoreCoinParser) UnpackTxid(buf []byte) (string, error) { 345 return hexutil.Encode(buf), nil 346 } 347 348 // PackBlockHash packs block hash to byte array 349 func (p *CoreCoinParser) PackBlockHash(hash string) ([]byte, error) { 350 if has0xPrefix(hash) { 351 hash = hash[2:] 352 } 353 return hex.DecodeString(hash) 354 } 355 356 // UnpackBlockHash unpacks byte array to block hash 357 func (p *CoreCoinParser) UnpackBlockHash(buf []byte) (string, error) { 358 return hexutil.Encode(buf), nil 359 } 360 361 // GetChainType returns CoreCoinType 362 func (p *CoreCoinParser) GetChainType() bchain.ChainType { 363 return bchain.ChainCoreCoinType 364 } 365 366 // GetHeightFromTx returns core coin specific data from bchain.Tx 367 func GetHeightFromTx(tx *bchain.Tx) (uint32, error) { 368 var bn string 369 csd, ok := tx.CoinSpecificData.(CoreCoinSpecificData) 370 if !ok { 371 return 0, errors.New("Missing CoinSpecificData") 372 } 373 bn = csd.Tx.BlockNumber 374 n, err := hexutil.DecodeUint64(bn) 375 if err != nil { 376 return 0, errors.Annotatef(err, "BlockNumber %v", bn) 377 } 378 return uint32(n), nil 379 } 380 381 // CoreCoinTypeGetTokenTransfersFromTx returns tokens data from bchain.Tx 382 func (p *CoreCoinParser) CoreCoinTypeGetTokenTransfersFromTx(tx *bchain.Tx) (bchain.TokenTransfers, error) { 383 var r bchain.TokenTransfers 384 var err error 385 csd, ok := tx.CoinSpecificData.(CoreCoinSpecificData) 386 if ok { 387 if csd.Receipt != nil { 388 r, err = getTokenTransfersFromLog(csd.Receipt.Logs) 389 } else { 390 r, err = getTokenTransfersFromTx(csd.Tx) 391 } 392 if err != nil { 393 return nil, err 394 } 395 } 396 return r, nil 397 } 398 399 // TxStatus is status of transaction 400 type TxStatus int 401 402 // statuses of transaction 403 const ( 404 TxStatusUnknown = TxStatus(iota - 2) 405 TxStatusPending 406 TxStatusFailure 407 TxStatusOK 408 ) 409 410 // CoreCoinTxData contains core coin specific transaction data 411 type CoreCoinTxData struct { 412 Status TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown 413 Nonce uint64 `json:"nonce"` 414 EnergyLimit *big.Int `json:"energylimit"` 415 EnergyUsed *big.Int `json:"energyused"` 416 EnergyPrice *big.Int `json:"energyprice"` 417 Data string `json:"data"` 418 } 419 420 // GetCoreCoinTxData returns CoreCoinTxData from bchain.Tx 421 func GetCoreCoinTxData(tx *bchain.Tx) *CoreCoinTxData { 422 return GetCoreCoinTxDataFromSpecificData(tx.CoinSpecificData) 423 } 424 425 // GetCoreCoinTxDataFromSpecificData returns CoreCoinTxData from coinSpecificData 426 func GetCoreCoinTxDataFromSpecificData(coinSpecificData interface{}) *CoreCoinTxData { 427 etd := CoreCoinTxData{Status: TxStatusPending} 428 csd, ok := coinSpecificData.(CoreCoinSpecificData) 429 if ok { 430 if csd.Tx != nil { 431 etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce) 432 etd.EnergyLimit, _ = hexutil.DecodeBig(csd.Tx.EnergyLimit) 433 etd.EnergyPrice, _ = hexutil.DecodeBig(csd.Tx.EnergyPrice) 434 etd.Data = csd.Tx.Payload 435 } 436 if csd.Receipt != nil { 437 switch csd.Receipt.Status { 438 case "0x1": 439 etd.Status = TxStatusOK 440 case "": // old transactions did not set status 441 etd.Status = TxStatusUnknown 442 default: 443 etd.Status = TxStatusFailure 444 } 445 etd.EnergyUsed, _ = hexutil.DecodeBig(csd.Receipt.EnergyUsed) 446 } 447 } 448 return &etd 449 }