github.com/cerberus-wallet/blockbook@v0.3.2/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, fixEIP55 bool) (*bchain.Tx, error) { 100 txid := tx.Hash 101 var ( 102 fa, ta []string 103 err error 104 ) 105 if len(tx.From) > 2 { 106 if fixEIP55 { 107 tx.From = EIP55AddressFromAddress(tx.From) 108 } 109 fa = []string{tx.From} 110 } 111 if len(tx.To) > 2 { 112 if fixEIP55 { 113 tx.To = EIP55AddressFromAddress(tx.To) 114 } 115 ta = []string{tx.To} 116 } 117 if fixEIP55 && receipt != nil && receipt.Logs != nil { 118 for _, l := range receipt.Logs { 119 if len(l.Address) > 2 { 120 l.Address = EIP55AddressFromAddress(l.Address) 121 } 122 } 123 } 124 ct := completeTransaction{ 125 Tx: tx, 126 Receipt: receipt, 127 } 128 vs, err := hexutil.DecodeBig(tx.Value) 129 if err != nil { 130 return nil, err 131 } 132 return &bchain.Tx{ 133 Blocktime: blocktime, 134 Confirmations: confirmations, 135 // Hex 136 // LockTime 137 Time: blocktime, 138 Txid: txid, 139 Vin: []bchain.Vin{ 140 { 141 Addresses: fa, 142 // Coinbase 143 // ScriptSig 144 // Sequence 145 // Txid 146 // Vout 147 }, 148 }, 149 Vout: []bchain.Vout{ 150 { 151 N: 0, // there is always up to one To address 152 ValueSat: *vs, 153 ScriptPubKey: bchain.ScriptPubKey{ 154 // Hex 155 Addresses: ta, 156 }, 157 }, 158 }, 159 CoinSpecificData: ct, 160 }, nil 161 } 162 163 // GetAddrDescFromVout returns internal address representation of given transaction output 164 func (p *EthereumParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressDescriptor, error) { 165 if len(output.ScriptPubKey.Addresses) != 1 { 166 return nil, bchain.ErrAddressMissing 167 } 168 return p.GetAddrDescFromAddress(output.ScriptPubKey.Addresses[0]) 169 } 170 171 func has0xPrefix(s string) bool { 172 return len(s) >= 2 && s[0] == '0' && (s[1]|32) == 'x' 173 } 174 175 // GetAddrDescFromAddress returns internal address representation of given address 176 func (p *EthereumParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) { 177 // github.com/ethereum/go-ethereum/common.HexToAddress does not handle address errors, using own decoding 178 if has0xPrefix(address) { 179 address = address[2:] 180 } 181 if len(address) != EthereumTypeAddressDescriptorLen*2 { 182 return nil, bchain.ErrAddressMissing 183 } 184 return hex.DecodeString(address) 185 } 186 187 // EIP55Address returns an EIP55-compliant hex string representation of the address 188 func EIP55Address(addrDesc bchain.AddressDescriptor) string { 189 raw := hexutil.Encode(addrDesc) 190 if len(raw) != 42 { 191 return raw 192 } 193 sha := sha3.NewLegacyKeccak256() 194 result := []byte(raw) 195 sha.Write(result[2:]) 196 hash := sha.Sum(nil) 197 198 for i := 2; i < len(result); i++ { 199 hashByte := hash[(i-2)>>1] 200 if i%2 == 0 { 201 hashByte = hashByte >> 4 202 } else { 203 hashByte &= 0xf 204 } 205 if result[i] > '9' && hashByte > 7 { 206 result[i] -= 32 207 } 208 } 209 return string(result) 210 } 211 212 // EIP55AddressFromAddress returns an EIP55-compliant hex string representation of the address 213 func EIP55AddressFromAddress(address string) string { 214 if has0xPrefix(address) { 215 address = address[2:] 216 } 217 b, err := hex.DecodeString(address) 218 if err != nil { 219 return address 220 } 221 return EIP55Address(b) 222 } 223 224 // GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable 225 func (p *EthereumParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) { 226 return []string{EIP55Address(addrDesc)}, true, nil 227 } 228 229 // GetScriptFromAddrDesc returns output script for given address descriptor 230 func (p *EthereumParser) GetScriptFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]byte, error) { 231 return addrDesc, nil 232 } 233 234 func hexDecode(s string) ([]byte, error) { 235 b, err := hexutil.Decode(s) 236 if err != nil && err != hexutil.ErrEmptyString { 237 return nil, err 238 } 239 return b, nil 240 } 241 242 func hexDecodeBig(s string) ([]byte, error) { 243 b, err := hexutil.DecodeBig(s) 244 if err != nil { 245 return nil, err 246 } 247 return b.Bytes(), nil 248 } 249 250 func hexEncodeBig(b []byte) string { 251 var i big.Int 252 i.SetBytes(b) 253 return hexutil.EncodeBig(&i) 254 } 255 256 // PackTx packs transaction to byte array 257 func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { 258 var err error 259 var n uint64 260 r, ok := tx.CoinSpecificData.(completeTransaction) 261 if !ok { 262 return nil, errors.New("Missing CoinSpecificData") 263 } 264 pt := &ProtoCompleteTransaction{} 265 pt.Tx = &ProtoCompleteTransaction_TxType{} 266 if pt.Tx.AccountNonce, err = hexutil.DecodeUint64(r.Tx.AccountNonce); err != nil { 267 return nil, errors.Annotatef(err, "AccountNonce %v", r.Tx.AccountNonce) 268 } 269 // pt.BlockNumber = height 270 if n, err = hexutil.DecodeUint64(r.Tx.BlockNumber); err != nil { 271 return nil, errors.Annotatef(err, "BlockNumber %v", r.Tx.BlockNumber) 272 } 273 pt.BlockNumber = uint32(n) 274 pt.BlockTime = uint64(blockTime) 275 if pt.Tx.From, err = hexDecode(r.Tx.From); err != nil { 276 return nil, errors.Annotatef(err, "From %v", r.Tx.From) 277 } 278 if pt.Tx.GasLimit, err = hexutil.DecodeUint64(r.Tx.GasLimit); err != nil { 279 return nil, errors.Annotatef(err, "GasLimit %v", r.Tx.GasLimit) 280 } 281 if pt.Tx.Hash, err = hexDecode(r.Tx.Hash); err != nil { 282 return nil, errors.Annotatef(err, "Hash %v", r.Tx.Hash) 283 } 284 if pt.Tx.Payload, err = hexDecode(r.Tx.Payload); err != nil { 285 return nil, errors.Annotatef(err, "Payload %v", r.Tx.Payload) 286 } 287 if pt.Tx.GasPrice, err = hexDecodeBig(r.Tx.GasPrice); err != nil { 288 return nil, errors.Annotatef(err, "Price %v", r.Tx.GasPrice) 289 } 290 // if pt.R, err = hexDecodeBig(r.R); err != nil { 291 // return nil, errors.Annotatef(err, "R %v", r.R) 292 // } 293 // if pt.S, err = hexDecodeBig(r.S); err != nil { 294 // return nil, errors.Annotatef(err, "S %v", r.S) 295 // } 296 // if pt.V, err = hexDecodeBig(r.V); err != nil { 297 // return nil, errors.Annotatef(err, "V %v", r.V) 298 // } 299 if pt.Tx.To, err = hexDecode(r.Tx.To); err != nil { 300 return nil, errors.Annotatef(err, "To %v", r.Tx.To) 301 } 302 if n, err = hexutil.DecodeUint64(r.Tx.TransactionIndex); err != nil { 303 return nil, errors.Annotatef(err, "TransactionIndex %v", r.Tx.TransactionIndex) 304 } 305 pt.Tx.TransactionIndex = uint32(n) 306 if pt.Tx.Value, err = hexDecodeBig(r.Tx.Value); err != nil { 307 return nil, errors.Annotatef(err, "Value %v", r.Tx.Value) 308 } 309 if r.Receipt != nil { 310 pt.Receipt = &ProtoCompleteTransaction_ReceiptType{} 311 if pt.Receipt.GasUsed, err = hexDecodeBig(r.Receipt.GasUsed); err != nil { 312 return nil, errors.Annotatef(err, "GasUsed %v", r.Receipt.GasUsed) 313 } 314 if pt.Receipt.Status, err = hexDecodeBig(r.Receipt.Status); err != nil { 315 return nil, errors.Annotatef(err, "Status %v", r.Receipt.Status) 316 } 317 ptLogs := make([]*ProtoCompleteTransaction_ReceiptType_LogType, len(r.Receipt.Logs)) 318 for i, l := range r.Receipt.Logs { 319 a, err := hexutil.Decode(l.Address) 320 if err != nil { 321 return nil, errors.Annotatef(err, "Address cannot be decoded %v", l) 322 } 323 d, err := hexutil.Decode(l.Data) 324 if err != nil { 325 return nil, errors.Annotatef(err, "Data cannot be decoded %v", l) 326 } 327 t := make([][]byte, len(l.Topics)) 328 for j, s := range l.Topics { 329 t[j], err = hexutil.Decode(s) 330 if err != nil { 331 return nil, errors.Annotatef(err, "Topic cannot be decoded %v", l) 332 } 333 } 334 ptLogs[i] = &ProtoCompleteTransaction_ReceiptType_LogType{ 335 Address: a, 336 Data: d, 337 Topics: t, 338 } 339 340 } 341 pt.Receipt.Log = ptLogs 342 } 343 return proto.Marshal(pt) 344 } 345 346 // UnpackTx unpacks transaction from byte array 347 func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { 348 var pt ProtoCompleteTransaction 349 err := proto.Unmarshal(buf, &pt) 350 if err != nil { 351 return nil, 0, err 352 } 353 rt := rpcTransaction{ 354 AccountNonce: hexutil.EncodeUint64(pt.Tx.AccountNonce), 355 BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)), 356 From: EIP55Address(pt.Tx.From), 357 GasLimit: hexutil.EncodeUint64(pt.Tx.GasLimit), 358 Hash: hexutil.Encode(pt.Tx.Hash), 359 Payload: hexutil.Encode(pt.Tx.Payload), 360 GasPrice: hexEncodeBig(pt.Tx.GasPrice), 361 // R: hexEncodeBig(pt.R), 362 // S: hexEncodeBig(pt.S), 363 // V: hexEncodeBig(pt.V), 364 To: EIP55Address(pt.Tx.To), 365 TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)), 366 Value: hexEncodeBig(pt.Tx.Value), 367 } 368 var rr *rpcReceipt 369 if pt.Receipt != nil { 370 logs := make([]*rpcLog, len(pt.Receipt.Log)) 371 for i, l := range pt.Receipt.Log { 372 topics := make([]string, len(l.Topics)) 373 for j, t := range l.Topics { 374 topics[j] = hexutil.Encode(t) 375 } 376 logs[i] = &rpcLog{ 377 Address: EIP55Address(l.Address), 378 Data: hexutil.Encode(l.Data), 379 Topics: topics, 380 } 381 } 382 rr = &rpcReceipt{ 383 GasUsed: hexEncodeBig(pt.Receipt.GasUsed), 384 Status: hexEncodeBig(pt.Receipt.Status), 385 Logs: logs, 386 } 387 } 388 tx, err := p.ethTxToTx(&rt, rr, int64(pt.BlockTime), 0, false) 389 if err != nil { 390 return nil, 0, err 391 } 392 return tx, pt.BlockNumber, nil 393 } 394 395 // PackedTxidLen returns length in bytes of packed txid 396 func (p *EthereumParser) PackedTxidLen() int { 397 return 32 398 } 399 400 // PackTxid packs txid to byte array 401 func (p *EthereumParser) PackTxid(txid string) ([]byte, error) { 402 if has0xPrefix(txid) { 403 txid = txid[2:] 404 } 405 return hex.DecodeString(txid) 406 } 407 408 // UnpackTxid unpacks byte array to txid 409 func (p *EthereumParser) UnpackTxid(buf []byte) (string, error) { 410 return hexutil.Encode(buf), nil 411 } 412 413 // PackBlockHash packs block hash to byte array 414 func (p *EthereumParser) PackBlockHash(hash string) ([]byte, error) { 415 if has0xPrefix(hash) { 416 hash = hash[2:] 417 } 418 return hex.DecodeString(hash) 419 } 420 421 // UnpackBlockHash unpacks byte array to block hash 422 func (p *EthereumParser) UnpackBlockHash(buf []byte) (string, error) { 423 return hexutil.Encode(buf), nil 424 } 425 426 // GetChainType returns EthereumType 427 func (p *EthereumParser) GetChainType() bchain.ChainType { 428 return bchain.ChainEthereumType 429 } 430 431 // GetHeightFromTx returns ethereum specific data from bchain.Tx 432 func GetHeightFromTx(tx *bchain.Tx) (uint32, error) { 433 var bn string 434 csd, ok := tx.CoinSpecificData.(completeTransaction) 435 if !ok { 436 return 0, errors.New("Missing CoinSpecificData") 437 } 438 bn = csd.Tx.BlockNumber 439 n, err := hexutil.DecodeUint64(bn) 440 if err != nil { 441 return 0, errors.Annotatef(err, "BlockNumber %v", bn) 442 } 443 return uint32(n), nil 444 } 445 446 // EthereumTypeGetErc20FromTx returns Erc20 data from bchain.Tx 447 func (p *EthereumParser) EthereumTypeGetErc20FromTx(tx *bchain.Tx) ([]bchain.Erc20Transfer, error) { 448 var r []bchain.Erc20Transfer 449 var err error 450 csd, ok := tx.CoinSpecificData.(completeTransaction) 451 if ok { 452 if csd.Receipt != nil { 453 r, err = erc20GetTransfersFromLog(csd.Receipt.Logs) 454 } else { 455 r, err = erc20GetTransfersFromTx(csd.Tx) 456 } 457 if err != nil { 458 return nil, err 459 } 460 } 461 return r, nil 462 } 463 464 const ( 465 txStatusUnknown = iota - 2 466 txStatusPending 467 txStatusFailure 468 txStatusOK 469 ) 470 471 // EthereumTxData contains ethereum specific transaction data 472 type EthereumTxData struct { 473 Status int `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown 474 Nonce uint64 `json:"nonce"` 475 GasLimit *big.Int `json:"gaslimit"` 476 GasUsed *big.Int `json:"gasused"` 477 GasPrice *big.Int `json:"gasprice"` 478 } 479 480 // GetEthereumTxData returns EthereumTxData from bchain.Tx 481 func GetEthereumTxData(tx *bchain.Tx) *EthereumTxData { 482 etd := EthereumTxData{Status: txStatusPending} 483 csd, ok := tx.CoinSpecificData.(completeTransaction) 484 if ok { 485 if csd.Tx != nil { 486 etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce) 487 etd.GasLimit, _ = hexutil.DecodeBig(csd.Tx.GasLimit) 488 etd.GasPrice, _ = hexutil.DecodeBig(csd.Tx.GasPrice) 489 } 490 if csd.Receipt != nil { 491 switch csd.Receipt.Status { 492 case "0x1": 493 etd.Status = txStatusOK 494 case "": // old transactions did not set status 495 etd.Status = txStatusUnknown 496 default: 497 etd.Status = txStatusFailure 498 } 499 etd.GasUsed, _ = hexutil.DecodeBig(csd.Receipt.GasUsed) 500 } 501 } 502 return &etd 503 }