github.com/dawnbass68/maddcash@v0.0.0-20201001105353-c91c12cb36e5/bchain/coins/eth/ethparser.go (about) 1 package eth 2 3 import ( 4 "blockbook/bchain" 5 "encoding/hex" 6 "math/big" 7 "strconv" 8 9 "github.com/ethereum/go-ethereum/common/hexutil" 10 "github.com/golang/protobuf/proto" 11 "github.com/juju/errors" 12 "golang.org/x/crypto/sha3" 13 ) 14 15 // EthereumTypeAddressDescriptorLen - in case of EthereumType, the AddressDescriptor has fixed length 16 const EthereumTypeAddressDescriptorLen = 20 17 18 // EtherAmountDecimalPoint defines number of decimal points in Ether amounts 19 const EtherAmountDecimalPoint = 18 20 21 // EthereumParser handle 22 type EthereumParser struct { 23 *bchain.BaseParser 24 } 25 26 // NewEthereumParser returns new EthereumParser instance 27 func NewEthereumParser(b int) *EthereumParser { 28 return &EthereumParser{&bchain.BaseParser{ 29 BlockAddressesToKeep: b, 30 AmountDecimalPoint: EtherAmountDecimalPoint, 31 }} 32 } 33 34 type rpcHeader struct { 35 Hash string `json:"hash"` 36 ParentHash string `json:"parentHash"` 37 Difficulty string `json:"difficulty"` 38 Number string `json:"number"` 39 Time string `json:"timestamp"` 40 Size string `json:"size"` 41 Nonce string `json:"nonce"` 42 } 43 44 type rpcTransaction struct { 45 AccountNonce string `json:"nonce"` 46 GasPrice string `json:"gasPrice"` 47 GasLimit string `json:"gas"` 48 To string `json:"to"` // nil means contract creation 49 Value string `json:"value"` 50 Payload string `json:"input"` 51 Hash string `json:"hash"` 52 BlockNumber string `json:"blockNumber"` 53 BlockHash string `json:"blockHash,omitempty"` 54 From string `json:"from"` 55 TransactionIndex string `json:"transactionIndex"` 56 // Signature values - ignored 57 // V string `json:"v"` 58 // R string `json:"r"` 59 // S string `json:"s"` 60 } 61 62 type rpcLog struct { 63 Address string `json:"address"` 64 Topics []string `json:"topics"` 65 Data string `json:"data"` 66 } 67 68 type rpcLogWithTxHash struct { 69 rpcLog 70 Hash string `json:"transactionHash"` 71 } 72 73 type rpcReceipt struct { 74 GasUsed string `json:"gasUsed"` 75 Status string `json:"status"` 76 Logs []*rpcLog `json:"logs"` 77 } 78 79 type completeTransaction struct { 80 Tx *rpcTransaction `json:"tx"` 81 Receipt *rpcReceipt `json:"receipt,omitempty"` 82 } 83 84 type rpcBlockTransactions struct { 85 Transactions []rpcTransaction `json:"transactions"` 86 } 87 88 type rpcBlockTxids struct { 89 Transactions []string `json:"transactions"` 90 } 91 92 func ethNumber(n string) (int64, error) { 93 if len(n) > 2 { 94 return strconv.ParseInt(n[2:], 16, 64) 95 } 96 return 0, errors.Errorf("Not a number: '%v'", n) 97 } 98 99 func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, blocktime int64, confirmations uint32) (*bchain.Tx, error) { 100 txid := tx.Hash 101 var ( 102 fa, ta []string 103 err error 104 ) 105 if len(tx.From) > 2 { 106 fa = []string{tx.From} 107 } 108 if len(tx.To) > 2 { 109 ta = []string{tx.To} 110 } 111 ct := completeTransaction{ 112 Tx: tx, 113 Receipt: receipt, 114 } 115 vs, err := hexutil.DecodeBig(tx.Value) 116 if err != nil { 117 return nil, err 118 } 119 return &bchain.Tx{ 120 Blocktime: blocktime, 121 Confirmations: confirmations, 122 // Hex 123 // LockTime 124 Time: blocktime, 125 Txid: txid, 126 Vin: []bchain.Vin{ 127 { 128 Addresses: fa, 129 // Coinbase 130 // ScriptSig 131 // Sequence 132 // Txid 133 // Vout 134 }, 135 }, 136 Vout: []bchain.Vout{ 137 { 138 N: 0, // there is always up to one To address 139 ValueSat: *vs, 140 ScriptPubKey: bchain.ScriptPubKey{ 141 // Hex 142 Addresses: ta, 143 }, 144 }, 145 }, 146 CoinSpecificData: ct, 147 }, nil 148 } 149 150 // GetAddrDescFromVout returns internal address representation of given transaction output 151 func (p *EthereumParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) { 152 if len(output.ScriptPubKey.Addresses) != 1 { 153 return nil, bchain.ErrAddressMissing 154 } 155 return p.GetAddrDescFromAddress(output.ScriptPubKey.Addresses[0]) 156 } 157 158 func has0xPrefix(s string) bool { 159 return len(s) >= 2 && s[0] == '0' && (s[1]|32) == 'x' 160 } 161 162 // GetAddrDescFromAddress returns internal address representation of given address 163 func (p *EthereumParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) { 164 // github.com/ethereum/go-ethereum/common.HexToAddress does not handle address errors, using own decoding 165 if has0xPrefix(address) { 166 address = address[2:] 167 } 168 if len(address) != EthereumTypeAddressDescriptorLen*2 { 169 return nil, bchain.ErrAddressMissing 170 } 171 return hex.DecodeString(address) 172 } 173 174 // EIP55Address returns an EIP55-compliant hex string representation of the address 175 func EIP55Address(addrDesc bchain.AddressDescriptor) string { 176 raw := hexutil.Encode(addrDesc) 177 if len(raw) != 42 { 178 return raw 179 } 180 sha := sha3.NewLegacyKeccak256() 181 result := []byte(raw) 182 sha.Write(result[2:]) 183 hash := sha.Sum(nil) 184 185 for i := 2; i < len(result); i++ { 186 hashByte := hash[(i-2)>>1] 187 if i%2 == 0 { 188 hashByte = hashByte >> 4 189 } else { 190 hashByte &= 0xf 191 } 192 if result[i] > '9' && hashByte > 7 { 193 result[i] -= 32 194 } 195 } 196 return string(result) 197 } 198 199 // EIP55AddressFromAddress returns an EIP55-compliant hex string representation of the address 200 func EIP55AddressFromAddress(address string) string { 201 b, err := hex.DecodeString(address) 202 if err != nil { 203 return address 204 } 205 return EIP55Address(b) 206 } 207 208 // GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable 209 func (p *EthereumParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) { 210 return []string{EIP55Address(addrDesc)}, true, nil 211 } 212 213 // GetScriptFromAddrDesc returns output script for given address descriptor 214 func (p *EthereumParser) GetScriptFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]byte, error) { 215 return addrDesc, nil 216 } 217 218 func hexDecode(s string) ([]byte, error) { 219 b, err := hexutil.Decode(s) 220 if err != nil && err != hexutil.ErrEmptyString { 221 return nil, err 222 } 223 return b, nil 224 } 225 226 func hexDecodeBig(s string) ([]byte, error) { 227 b, err := hexutil.DecodeBig(s) 228 if err != nil { 229 return nil, err 230 } 231 return b.Bytes(), nil 232 } 233 234 func hexEncodeBig(b []byte) string { 235 var i big.Int 236 i.SetBytes(b) 237 return hexutil.EncodeBig(&i) 238 } 239 240 // PackTx packs transaction to byte array 241 func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { 242 var err error 243 var n uint64 244 r, ok := tx.CoinSpecificData.(completeTransaction) 245 if !ok { 246 return nil, errors.New("Missing CoinSpecificData") 247 } 248 pt := &ProtoCompleteTransaction{} 249 pt.Tx = &ProtoCompleteTransaction_TxType{} 250 if pt.Tx.AccountNonce, err = hexutil.DecodeUint64(r.Tx.AccountNonce); err != nil { 251 return nil, errors.Annotatef(err, "AccountNonce %v", r.Tx.AccountNonce) 252 } 253 // pt.BlockNumber = height 254 if n, err = hexutil.DecodeUint64(r.Tx.BlockNumber); err != nil { 255 return nil, errors.Annotatef(err, "BlockNumber %v", r.Tx.BlockNumber) 256 } 257 pt.BlockNumber = uint32(n) 258 pt.BlockTime = uint64(blockTime) 259 if pt.Tx.From, err = hexDecode(r.Tx.From); err != nil { 260 return nil, errors.Annotatef(err, "From %v", r.Tx.From) 261 } 262 if pt.Tx.GasLimit, err = hexutil.DecodeUint64(r.Tx.GasLimit); err != nil { 263 return nil, errors.Annotatef(err, "GasLimit %v", r.Tx.GasLimit) 264 } 265 if pt.Tx.Hash, err = hexDecode(r.Tx.Hash); err != nil { 266 return nil, errors.Annotatef(err, "Hash %v", r.Tx.Hash) 267 } 268 if pt.Tx.Payload, err = hexDecode(r.Tx.Payload); err != nil { 269 return nil, errors.Annotatef(err, "Payload %v", r.Tx.Payload) 270 } 271 if pt.Tx.GasPrice, err = hexDecodeBig(r.Tx.GasPrice); err != nil { 272 return nil, errors.Annotatef(err, "Price %v", r.Tx.GasPrice) 273 } 274 // if pt.R, err = hexDecodeBig(r.R); err != nil { 275 // return nil, errors.Annotatef(err, "R %v", r.R) 276 // } 277 // if pt.S, err = hexDecodeBig(r.S); err != nil { 278 // return nil, errors.Annotatef(err, "S %v", r.S) 279 // } 280 // if pt.V, err = hexDecodeBig(r.V); err != nil { 281 // return nil, errors.Annotatef(err, "V %v", r.V) 282 // } 283 if pt.Tx.To, err = hexDecode(r.Tx.To); err != nil { 284 return nil, errors.Annotatef(err, "To %v", r.Tx.To) 285 } 286 if n, err = hexutil.DecodeUint64(r.Tx.TransactionIndex); err != nil { 287 return nil, errors.Annotatef(err, "TransactionIndex %v", r.Tx.TransactionIndex) 288 } 289 pt.Tx.TransactionIndex = uint32(n) 290 if pt.Tx.Value, err = hexDecodeBig(r.Tx.Value); err != nil { 291 return nil, errors.Annotatef(err, "Value %v", r.Tx.Value) 292 } 293 if r.Receipt != nil { 294 pt.Receipt = &ProtoCompleteTransaction_ReceiptType{} 295 if pt.Receipt.GasUsed, err = hexDecodeBig(r.Receipt.GasUsed); err != nil { 296 return nil, errors.Annotatef(err, "GasUsed %v", r.Receipt.GasUsed) 297 } 298 if pt.Receipt.Status, err = hexDecodeBig(r.Receipt.Status); err != nil { 299 return nil, errors.Annotatef(err, "Status %v", r.Receipt.Status) 300 } 301 ptLogs := make([]*ProtoCompleteTransaction_ReceiptType_LogType, len(r.Receipt.Logs)) 302 for i, l := range r.Receipt.Logs { 303 a, err := hexutil.Decode(l.Address) 304 if err != nil { 305 return nil, errors.Annotatef(err, "Address cannot be decoded %v", l) 306 } 307 d, err := hexutil.Decode(l.Data) 308 if err != nil { 309 return nil, errors.Annotatef(err, "Data cannot be decoded %v", l) 310 } 311 t := make([][]byte, len(l.Topics)) 312 for j, s := range l.Topics { 313 t[j], err = hexutil.Decode(s) 314 if err != nil { 315 return nil, errors.Annotatef(err, "Topic cannot be decoded %v", l) 316 } 317 } 318 ptLogs[i] = &ProtoCompleteTransaction_ReceiptType_LogType{ 319 Address: a, 320 Data: d, 321 Topics: t, 322 } 323 324 } 325 pt.Receipt.Log = ptLogs 326 } 327 return proto.Marshal(pt) 328 } 329 330 // UnpackTx unpacks transaction from byte array 331 func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { 332 var pt ProtoCompleteTransaction 333 err := proto.Unmarshal(buf, &pt) 334 if err != nil { 335 return nil, 0, err 336 } 337 rt := rpcTransaction{ 338 AccountNonce: hexutil.EncodeUint64(pt.Tx.AccountNonce), 339 BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)), 340 From: EIP55Address(pt.Tx.From), 341 GasLimit: hexutil.EncodeUint64(pt.Tx.GasLimit), 342 Hash: hexutil.Encode(pt.Tx.Hash), 343 Payload: hexutil.Encode(pt.Tx.Payload), 344 GasPrice: hexEncodeBig(pt.Tx.GasPrice), 345 // R: hexEncodeBig(pt.R), 346 // S: hexEncodeBig(pt.S), 347 // V: hexEncodeBig(pt.V), 348 To: EIP55Address(pt.Tx.To), 349 TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)), 350 Value: hexEncodeBig(pt.Tx.Value), 351 } 352 var rr *rpcReceipt 353 if pt.Receipt != nil { 354 logs := make([]*rpcLog, len(pt.Receipt.Log)) 355 for i, l := range pt.Receipt.Log { 356 topics := make([]string, len(l.Topics)) 357 for j, t := range l.Topics { 358 topics[j] = hexutil.Encode(t) 359 } 360 logs[i] = &rpcLog{ 361 Address: EIP55Address(l.Address), 362 Data: hexutil.Encode(l.Data), 363 Topics: topics, 364 } 365 } 366 rr = &rpcReceipt{ 367 GasUsed: hexEncodeBig(pt.Receipt.GasUsed), 368 Status: hexEncodeBig(pt.Receipt.Status), 369 Logs: logs, 370 } 371 } 372 tx, err := p.ethTxToTx(&rt, rr, int64(pt.BlockTime), 0) 373 if err != nil { 374 return nil, 0, err 375 } 376 return tx, pt.BlockNumber, nil 377 } 378 379 // PackedTxidLen returns length in bytes of packed txid 380 func (p *EthereumParser) PackedTxidLen() int { 381 return 32 382 } 383 384 // PackTxid packs txid to byte array 385 func (p *EthereumParser) PackTxid(txid string) ([]byte, error) { 386 if has0xPrefix(txid) { 387 txid = txid[2:] 388 } 389 return hex.DecodeString(txid) 390 } 391 392 // UnpackTxid unpacks byte array to txid 393 func (p *EthereumParser) UnpackTxid(buf []byte) (string, error) { 394 return hexutil.Encode(buf), nil 395 } 396 397 // PackBlockHash packs block hash to byte array 398 func (p *EthereumParser) PackBlockHash(hash string) ([]byte, error) { 399 if has0xPrefix(hash) { 400 hash = hash[2:] 401 } 402 return hex.DecodeString(hash) 403 } 404 405 // UnpackBlockHash unpacks byte array to block hash 406 func (p *EthereumParser) UnpackBlockHash(buf []byte) (string, error) { 407 return hexutil.Encode(buf), nil 408 } 409 410 // GetChainType returns EthereumType 411 func (p *EthereumParser) GetChainType() bchain.ChainType { 412 return bchain.ChainEthereumType 413 } 414 415 // GetHeightFromTx returns ethereum specific data from bchain.Tx 416 func GetHeightFromTx(tx *bchain.Tx) (uint32, error) { 417 var bn string 418 csd, ok := tx.CoinSpecificData.(completeTransaction) 419 if !ok { 420 return 0, errors.New("Missing CoinSpecificData") 421 } 422 bn = csd.Tx.BlockNumber 423 n, err := hexutil.DecodeUint64(bn) 424 if err != nil { 425 return 0, errors.Annotatef(err, "BlockNumber %v", bn) 426 } 427 return uint32(n), nil 428 } 429 430 // EthereumTypeGetErc20FromTx returns Erc20 data from bchain.Tx 431 func (p *EthereumParser) EthereumTypeGetErc20FromTx(tx *bchain.Tx) ([]bchain.Erc20Transfer, error) { 432 var r []bchain.Erc20Transfer 433 var err error 434 csd, ok := tx.CoinSpecificData.(completeTransaction) 435 if ok { 436 if csd.Receipt != nil { 437 r, err = erc20GetTransfersFromLog(csd.Receipt.Logs) 438 } else { 439 r, err = erc20GetTransfersFromTx(csd.Tx) 440 } 441 if err != nil { 442 return nil, err 443 } 444 } 445 return r, nil 446 } 447 448 const ( 449 txStatusUnknown = iota - 2 450 txStatusPending 451 txStatusFailure 452 txStatusOK 453 ) 454 455 // EthereumTxData contains ethereum specific transaction data 456 type EthereumTxData struct { 457 Status int `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown 458 Nonce uint64 `json:"nonce"` 459 GasLimit *big.Int `json:"gaslimit"` 460 GasUsed *big.Int `json:"gasused"` 461 GasPrice *big.Int `json:"gasprice"` 462 } 463 464 // GetEthereumTxData returns EthereumTxData from bchain.Tx 465 func GetEthereumTxData(tx *bchain.Tx) *EthereumTxData { 466 etd := EthereumTxData{Status: txStatusPending} 467 csd, ok := tx.CoinSpecificData.(completeTransaction) 468 if ok { 469 if csd.Tx != nil { 470 etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce) 471 etd.GasLimit, _ = hexutil.DecodeBig(csd.Tx.GasLimit) 472 etd.GasPrice, _ = hexutil.DecodeBig(csd.Tx.GasPrice) 473 } 474 if csd.Receipt != nil { 475 switch csd.Receipt.Status { 476 case "0x1": 477 etd.Status = txStatusOK 478 case "": // old transactions did not set status 479 etd.Status = txStatusUnknown 480 default: 481 etd.Status = txStatusFailure 482 } 483 etd.GasUsed, _ = hexutil.DecodeBig(csd.Receipt.GasUsed) 484 } 485 } 486 return &etd 487 }