github.com/cryptohub-digital/blockbook-fork@v0.0.0-20230713133354-673c927af7f1/bchain/coins/eth/ethrpc.go (about) 1 package eth 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "math/big" 9 "net/http" 10 "strconv" 11 "strings" 12 "sync" 13 "time" 14 15 "github.com/cryptohub-digital/blockbook-fork/bchain" 16 "github.com/cryptohub-digital/blockbook-fork/common" 17 "github.com/ethereum/go-ethereum" 18 ethcommon "github.com/ethereum/go-ethereum/common" 19 "github.com/ethereum/go-ethereum/common/hexutil" 20 "github.com/ethereum/go-ethereum/core/types" 21 "github.com/ethereum/go-ethereum/ethclient" 22 "github.com/ethereum/go-ethereum/rpc" 23 "github.com/golang/glog" 24 "github.com/juju/errors" 25 ) 26 27 // Network type specifies the type of ethereum network 28 type Network uint32 29 30 const ( 31 // MainNet is production network 32 MainNet Network = 1 33 // TestNet is Ropsten test network 34 TestNet Network = 3 35 // TestNetGoerli is Goerli test network 36 TestNetGoerli Network = 5 37 // TestNetSepolia is Sepolia test network 38 TestNetSepolia Network = 11155111 39 ) 40 41 // Configuration represents json config file 42 type Configuration struct { 43 CoinName string `json:"coin_name"` 44 CoinShortcut string `json:"coin_shortcut"` 45 RPCURL string `json:"rpc_url"` 46 RPCTimeout int `json:"rpc_timeout"` 47 BlockAddressesToKeep int `json:"block_addresses_to_keep"` 48 AddressAliases bool `json:"address_aliases,omitempty"` 49 MempoolTxTimeoutHours int `json:"mempoolTxTimeoutHours"` 50 QueryBackendOnMempoolResync bool `json:"queryBackendOnMempoolResync"` 51 ProcessInternalTransactions bool `json:"processInternalTransactions"` 52 ProcessZeroInternalTransactions bool `json:"processZeroInternalTransactions"` 53 ConsensusNodeVersionURL string `json:"consensusNodeVersion"` 54 } 55 56 // EthereumRPC is an interface to JSON-RPC eth service. 57 type EthereumRPC struct { 58 *bchain.BaseChain 59 Client bchain.EVMClient 60 RPC bchain.EVMRPCClient 61 MainNetChainID Network 62 Timeout time.Duration 63 Parser *EthereumParser 64 PushHandler func(bchain.NotificationType) 65 OpenRPC func(string) (bchain.EVMRPCClient, bchain.EVMClient, error) 66 Mempool *bchain.MempoolEthereumType 67 mempoolInitialized bool 68 bestHeaderLock sync.Mutex 69 bestHeader bchain.EVMHeader 70 bestHeaderTime time.Time 71 NewBlock bchain.EVMNewBlockSubscriber 72 newBlockSubscription bchain.EVMClientSubscription 73 NewTx bchain.EVMNewTxSubscriber 74 newTxSubscription bchain.EVMClientSubscription 75 ChainConfig *Configuration 76 } 77 78 // ProcessInternalTransactions specifies if internal transactions are processed 79 var ProcessInternalTransactions bool 80 81 // NewEthereumRPC returns new EthRPC instance. 82 func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) { 83 var err error 84 var c Configuration 85 err = json.Unmarshal(config, &c) 86 if err != nil { 87 return nil, errors.Annotatef(err, "Invalid configuration file") 88 } 89 // keep at least 100 mappings block->addresses to allow rollback 90 if c.BlockAddressesToKeep < 100 { 91 c.BlockAddressesToKeep = 100 92 } 93 94 s := &EthereumRPC{ 95 BaseChain: &bchain.BaseChain{}, 96 ChainConfig: &c, 97 } 98 99 ProcessInternalTransactions = c.ProcessInternalTransactions 100 101 // always create parser 102 s.Parser = NewEthereumParser(c.BlockAddressesToKeep, c.AddressAliases) 103 s.Timeout = time.Duration(c.RPCTimeout) * time.Second 104 s.PushHandler = pushHandler 105 106 return s, nil 107 } 108 109 // Initialize initializes ethereum rpc interface 110 func (b *EthereumRPC) Initialize() error { 111 b.OpenRPC = func(url string) (bchain.EVMRPCClient, bchain.EVMClient, error) { 112 r, err := rpc.Dial(url) 113 if err != nil { 114 return nil, nil, err 115 } 116 rc := &EthereumRPCClient{Client: r} 117 ec := &EthereumClient{Client: ethclient.NewClient(r)} 118 return rc, ec, nil 119 } 120 121 rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL) 122 if err != nil { 123 return err 124 } 125 126 // set chain specific 127 b.Client = ec 128 b.RPC = rc 129 b.MainNetChainID = MainNet 130 b.NewBlock = &EthereumNewBlock{channel: make(chan *types.Header)} 131 b.NewTx = &EthereumNewTx{channel: make(chan ethcommon.Hash)} 132 133 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 134 defer cancel() 135 136 id, err := b.Client.NetworkID(ctx) 137 if err != nil { 138 return err 139 } 140 141 // parameters for getInfo request 142 switch Network(id.Uint64()) { 143 case MainNet: 144 b.Testnet = false 145 b.Network = "livenet" 146 case TestNet: 147 b.Testnet = true 148 b.Network = "testnet" 149 case TestNetGoerli: 150 b.Testnet = true 151 b.Network = "goerli" 152 case TestNetSepolia: 153 b.Testnet = true 154 b.Network = "sepolia" 155 default: 156 return errors.Errorf("Unknown network id %v", id) 157 } 158 glog.Info("rpc: block chain ", b.Network) 159 160 return nil 161 } 162 163 // CreateMempool creates mempool if not already created, however does not initialize it 164 func (b *EthereumRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) { 165 if b.Mempool == nil { 166 b.Mempool = bchain.NewMempoolEthereumType(chain, b.ChainConfig.MempoolTxTimeoutHours, b.ChainConfig.QueryBackendOnMempoolResync) 167 glog.Info("mempool created, MempoolTxTimeoutHours=", b.ChainConfig.MempoolTxTimeoutHours, ", QueryBackendOnMempoolResync=", b.ChainConfig.QueryBackendOnMempoolResync) 168 } 169 return b.Mempool, nil 170 } 171 172 // InitializeMempool creates subscriptions to newHeads and newPendingTransactions 173 func (b *EthereumRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc, onNewTx bchain.OnNewTxFunc) error { 174 if b.Mempool == nil { 175 return errors.New("Mempool not created") 176 } 177 178 // get initial mempool transactions 179 txs, err := b.GetMempoolTransactions() 180 if err != nil { 181 return err 182 } 183 for _, txid := range txs { 184 b.Mempool.AddTransactionToMempool(txid) 185 } 186 187 b.Mempool.OnNewTxAddr = onNewTxAddr 188 b.Mempool.OnNewTx = onNewTx 189 190 if err = b.subscribeEvents(); err != nil { 191 return err 192 } 193 194 b.mempoolInitialized = true 195 196 return nil 197 } 198 199 func (b *EthereumRPC) subscribeEvents() error { 200 // new block notifications handling 201 go func() { 202 for { 203 h, ok := b.NewBlock.Read() 204 if !ok { 205 break 206 } 207 b.UpdateBestHeader(h) 208 // notify blockbook 209 b.PushHandler(bchain.NotificationNewBlock) 210 } 211 }() 212 213 // new block subscription 214 if err := b.subscribe(func() (bchain.EVMClientSubscription, error) { 215 // invalidate the previous subscription - it is either the first one or there was an error 216 b.newBlockSubscription = nil 217 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 218 defer cancel() 219 sub, err := b.RPC.EthSubscribe(ctx, b.NewBlock.Channel(), "newHeads") 220 if err != nil { 221 return nil, errors.Annotatef(err, "EthSubscribe newHeads") 222 } 223 b.newBlockSubscription = sub 224 glog.Info("Subscribed to newHeads") 225 return sub, nil 226 }); err != nil { 227 return err 228 } 229 230 // new mempool transaction notifications handling 231 go func() { 232 for { 233 t, ok := b.NewTx.Read() 234 if !ok { 235 break 236 } 237 hex := t.Hex() 238 if glog.V(2) { 239 glog.Info("rpc: new tx ", hex) 240 } 241 b.Mempool.AddTransactionToMempool(hex) 242 b.PushHandler(bchain.NotificationNewTx) 243 } 244 }() 245 246 // new mempool transaction subscription 247 if err := b.subscribe(func() (bchain.EVMClientSubscription, error) { 248 // invalidate the previous subscription - it is either the first one or there was an error 249 b.newTxSubscription = nil 250 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 251 defer cancel() 252 sub, err := b.RPC.EthSubscribe(ctx, b.NewTx.Channel(), "newPendingTransactions") 253 if err != nil { 254 return nil, errors.Annotatef(err, "EthSubscribe newPendingTransactions") 255 } 256 b.newTxSubscription = sub 257 glog.Info("Subscribed to newPendingTransactions") 258 return sub, nil 259 }); err != nil { 260 return err 261 } 262 263 return nil 264 } 265 266 // subscribe subscribes notification and tries to resubscribe in case of error 267 func (b *EthereumRPC) subscribe(f func() (bchain.EVMClientSubscription, error)) error { 268 s, err := f() 269 if err != nil { 270 return err 271 } 272 go func() { 273 Loop: 274 for { 275 // wait for error in subscription 276 e := <-s.Err() 277 // nil error means sub.Unsubscribe called, exit goroutine 278 if e == nil { 279 return 280 } 281 glog.Error("Subscription error ", e) 282 timer := time.NewTimer(time.Second * 2) 283 // try in 2 second interval to resubscribe 284 for { 285 select { 286 case e = <-s.Err(): 287 if e == nil { 288 return 289 } 290 case <-timer.C: 291 ns, err := f() 292 if err == nil { 293 // subscription successful, restart wait for next error 294 s = ns 295 continue Loop 296 } 297 glog.Error("Resubscribe error ", err) 298 timer.Reset(time.Second * 2) 299 } 300 } 301 } 302 }() 303 return nil 304 } 305 306 func (b *EthereumRPC) closeRPC() { 307 if b.newBlockSubscription != nil { 308 b.newBlockSubscription.Unsubscribe() 309 } 310 if b.newTxSubscription != nil { 311 b.newTxSubscription.Unsubscribe() 312 } 313 if b.RPC != nil { 314 b.RPC.Close() 315 } 316 } 317 318 func (b *EthereumRPC) reconnectRPC() error { 319 glog.Info("Reconnecting RPC") 320 b.closeRPC() 321 rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL) 322 if err != nil { 323 return err 324 } 325 b.RPC = rc 326 b.Client = ec 327 return b.subscribeEvents() 328 } 329 330 // Shutdown cleans up rpc interface to ethereum 331 func (b *EthereumRPC) Shutdown(ctx context.Context) error { 332 b.closeRPC() 333 b.NewBlock.Close() 334 b.NewTx.Close() 335 glog.Info("rpc: shutdown") 336 return nil 337 } 338 339 // GetCoinName returns coin name 340 func (b *EthereumRPC) GetCoinName() string { 341 return b.ChainConfig.CoinName 342 } 343 344 // GetSubversion returns empty string, ethereum does not have subversion 345 func (b *EthereumRPC) GetSubversion() string { 346 return "" 347 } 348 349 func (b *EthereumRPC) getConsensusVersion() string { 350 if b.ChainConfig.ConsensusNodeVersionURL == "" { 351 return "" 352 } 353 httpClient := &http.Client{ 354 Timeout: 2 * time.Second, 355 } 356 resp, err := httpClient.Get(b.ChainConfig.ConsensusNodeVersionURL) 357 if err != nil || resp.StatusCode != http.StatusOK { 358 glog.Error("getConsensusVersion ", err) 359 return "" 360 } 361 body, err := ioutil.ReadAll(resp.Body) 362 if err != nil { 363 glog.Error("getConsensusVersion ", err) 364 return "" 365 } 366 type consensusVersion struct { 367 Data struct { 368 Version string `json:"version"` 369 } `json:"data"` 370 } 371 var v consensusVersion 372 err = json.Unmarshal(body, &v) 373 if err != nil { 374 glog.Error("getConsensusVersion ", err) 375 return "" 376 } 377 return v.Data.Version 378 } 379 380 // GetChainInfo returns information about the connected backend 381 func (b *EthereumRPC) GetChainInfo() (*bchain.ChainInfo, error) { 382 h, err := b.getBestHeader() 383 if err != nil { 384 return nil, err 385 } 386 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 387 defer cancel() 388 id, err := b.Client.NetworkID(ctx) 389 if err != nil { 390 return nil, err 391 } 392 var ver string 393 if err := b.RPC.CallContext(ctx, &ver, "web3_clientVersion"); err != nil { 394 return nil, err 395 } 396 consensusVersion := b.getConsensusVersion() 397 rv := &bchain.ChainInfo{ 398 Blocks: int(h.Number().Int64()), 399 Bestblockhash: h.Hash(), 400 Difficulty: h.Difficulty().String(), 401 Version: ver, 402 ConsensusVersion: consensusVersion, 403 } 404 idi := int(id.Uint64()) 405 if idi == int(b.MainNetChainID) { 406 rv.Chain = "mainnet" 407 } else { 408 rv.Chain = "testnet " + strconv.Itoa(idi) 409 } 410 return rv, nil 411 } 412 413 func (b *EthereumRPC) getBestHeader() (bchain.EVMHeader, error) { 414 b.bestHeaderLock.Lock() 415 defer b.bestHeaderLock.Unlock() 416 // if the best header was not updated for 15 minutes, there could be a subscription problem, reconnect RPC 417 // do it only in case of normal operation, not initial synchronization 418 if b.bestHeaderTime.Add(15*time.Minute).Before(time.Now()) && !b.bestHeaderTime.IsZero() && b.mempoolInitialized { 419 err := b.reconnectRPC() 420 if err != nil { 421 return nil, err 422 } 423 b.bestHeader = nil 424 } 425 if b.bestHeader == nil { 426 var err error 427 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 428 defer cancel() 429 b.bestHeader, err = b.Client.HeaderByNumber(ctx, nil) 430 if err != nil { 431 b.bestHeader = nil 432 return nil, err 433 } 434 b.bestHeaderTime = time.Now() 435 } 436 return b.bestHeader, nil 437 } 438 439 // UpdateBestHeader keeps track of the latest block header confirmed on chain 440 func (b *EthereumRPC) UpdateBestHeader(h bchain.EVMHeader) { 441 glog.V(2).Info("rpc: new block header ", h.Number()) 442 b.bestHeaderLock.Lock() 443 b.bestHeader = h 444 b.bestHeaderTime = time.Now() 445 b.bestHeaderLock.Unlock() 446 } 447 448 // GetBestBlockHash returns hash of the tip of the best-block-chain 449 func (b *EthereumRPC) GetBestBlockHash() (string, error) { 450 h, err := b.getBestHeader() 451 if err != nil { 452 return "", err 453 } 454 return h.Hash(), nil 455 } 456 457 // GetBestBlockHeight returns height of the tip of the best-block-chain 458 func (b *EthereumRPC) GetBestBlockHeight() (uint32, error) { 459 h, err := b.getBestHeader() 460 if err != nil { 461 return 0, err 462 } 463 return uint32(h.Number().Uint64()), nil 464 } 465 466 // GetBlockHash returns hash of block in best-block-chain at given height 467 func (b *EthereumRPC) GetBlockHash(height uint32) (string, error) { 468 var n big.Int 469 n.SetUint64(uint64(height)) 470 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 471 defer cancel() 472 h, err := b.Client.HeaderByNumber(ctx, &n) 473 if err != nil { 474 if err == ethereum.NotFound { 475 return "", bchain.ErrBlockNotFound 476 } 477 return "", errors.Annotatef(err, "height %v", height) 478 } 479 return h.Hash(), nil 480 } 481 482 func (b *EthereumRPC) ethHeaderToBlockHeader(h *rpcHeader) (*bchain.BlockHeader, error) { 483 height, err := ethNumber(h.Number) 484 if err != nil { 485 return nil, err 486 } 487 c, err := b.computeConfirmations(uint64(height)) 488 if err != nil { 489 return nil, err 490 } 491 time, err := ethNumber(h.Time) 492 if err != nil { 493 return nil, err 494 } 495 size, err := ethNumber(h.Size) 496 if err != nil { 497 return nil, err 498 } 499 return &bchain.BlockHeader{ 500 Hash: h.Hash, 501 Prev: h.ParentHash, 502 Height: uint32(height), 503 Confirmations: int(c), 504 Time: time, 505 Size: int(size), 506 }, nil 507 } 508 509 // GetBlockHeader returns header of block with given hash 510 func (b *EthereumRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) { 511 raw, err := b.getBlockRaw(hash, 0, false) 512 if err != nil { 513 return nil, err 514 } 515 var h rpcHeader 516 if err := json.Unmarshal(raw, &h); err != nil { 517 return nil, errors.Annotatef(err, "hash %v", hash) 518 } 519 return b.ethHeaderToBlockHeader(&h) 520 } 521 522 func (b *EthereumRPC) computeConfirmations(n uint64) (uint32, error) { 523 bh, err := b.getBestHeader() 524 if err != nil { 525 return 0, err 526 } 527 bn := bh.Number().Uint64() 528 // transaction in the best block has 1 confirmation 529 return uint32(bn - n + 1), nil 530 } 531 532 func (b *EthereumRPC) getBlockRaw(hash string, height uint32, fullTxs bool) (json.RawMessage, error) { 533 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 534 defer cancel() 535 var raw json.RawMessage 536 var err error 537 if hash != "" { 538 if hash == "pending" { 539 err = b.RPC.CallContext(ctx, &raw, "eth_getBlockByNumber", hash, fullTxs) 540 } else { 541 err = b.RPC.CallContext(ctx, &raw, "eth_getBlockByHash", ethcommon.HexToHash(hash), fullTxs) 542 } 543 } else { 544 err = b.RPC.CallContext(ctx, &raw, "eth_getBlockByNumber", fmt.Sprintf("%#x", height), fullTxs) 545 } 546 if err != nil { 547 return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) 548 } else if len(raw) == 0 || (len(raw) == 4 && string(raw) == "null") { 549 return nil, bchain.ErrBlockNotFound 550 } 551 return raw, nil 552 } 553 554 func (b *EthereumRPC) processEventsForBlock(blockNumber string) (map[string][]*bchain.RpcLog, []bchain.AddressAliasRecord, error) { 555 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 556 defer cancel() 557 var logs []rpcLogWithTxHash 558 var ensRecords []bchain.AddressAliasRecord 559 err := b.RPC.CallContext(ctx, &logs, "eth_getLogs", map[string]interface{}{ 560 "fromBlock": blockNumber, 561 "toBlock": blockNumber, 562 }) 563 if err != nil { 564 return nil, nil, errors.Annotatef(err, "eth_getLogs blockNumber %v", blockNumber) 565 } 566 r := make(map[string][]*bchain.RpcLog) 567 for i := range logs { 568 l := &logs[i] 569 r[l.Hash] = append(r[l.Hash], &l.RpcLog) 570 ens := getEnsRecord(l) 571 if ens != nil { 572 ensRecords = append(ensRecords, *ens) 573 } 574 } 575 return r, ensRecords, nil 576 } 577 578 type rpcCallTrace struct { 579 // CREATE, CREATE2, SELFDESTRUCT, CALL, CALLCODE, DELEGATECALL, STATICCALL 580 Type string `json:"type"` 581 From string `json:"from"` 582 To string `json:"to"` 583 Value string `json:"value"` 584 Error string `json:"error"` 585 Output string `json:"output"` 586 Calls []rpcCallTrace `json:"calls"` 587 } 588 589 type rpcTraceResult struct { 590 Result rpcCallTrace `json:"result"` 591 } 592 593 func (b *EthereumRPC) getCreationContractInfo(contract string, height uint32) *bchain.ContractInfo { 594 ci, err := b.fetchContractInfo(contract) 595 if ci == nil || err != nil { 596 ci = &bchain.ContractInfo{ 597 Contract: contract, 598 } 599 } 600 ci.Type = bchain.UnknownTokenType 601 ci.CreatedInBlock = height 602 return ci 603 } 604 605 func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInternalData, contracts []bchain.ContractInfo, blockHeight uint32) []bchain.ContractInfo { 606 value, err := hexutil.DecodeBig(call.Value) 607 if call.Type == "CREATE" || call.Type == "CREATE2" { 608 d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ 609 Type: bchain.CREATE, 610 Value: *value, 611 From: call.From, 612 To: call.To, // new contract address 613 }) 614 contracts = append(contracts, *b.getCreationContractInfo(call.To, blockHeight)) 615 } else if call.Type == "SELFDESTRUCT" { 616 d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ 617 Type: bchain.SELFDESTRUCT, 618 Value: *value, 619 From: call.From, // destroyed contract address 620 To: call.To, 621 }) 622 contracts = append(contracts, bchain.ContractInfo{Contract: call.From, DestructedInBlock: blockHeight}) 623 } else if call.Type == "DELEGATECALL" { 624 // ignore DELEGATECALL (geth v1.11 the changed tracer behavior) 625 // https://github.com/ethereum/go-ethereum/issues/26726 626 } else if err == nil && (value.BitLen() > 0 || b.ChainConfig.ProcessZeroInternalTransactions) { 627 d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{ 628 Value: *value, 629 From: call.From, 630 To: call.To, 631 }) 632 } 633 if call.Error != "" { 634 d.Error = call.Error 635 } 636 for i := range call.Calls { 637 contracts = b.processCallTrace(&call.Calls[i], d, contracts, blockHeight) 638 } 639 return contracts 640 } 641 642 // getInternalDataForBlock fetches debug trace using callTracer, extracts internal transfers and creations and destructions of contracts 643 func (b *EthereumRPC) getInternalDataForBlock(blockHash string, blockHeight uint32, transactions []bchain.RpcTransaction) ([]bchain.EthereumInternalData, []bchain.ContractInfo, error) { 644 data := make([]bchain.EthereumInternalData, len(transactions)) 645 contracts := make([]bchain.ContractInfo, 0) 646 if ProcessInternalTransactions { 647 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 648 defer cancel() 649 var trace []rpcTraceResult 650 err := b.RPC.CallContext(ctx, &trace, "debug_traceBlockByHash", blockHash, map[string]interface{}{"tracer": "callTracer"}) 651 if err != nil { 652 glog.Error("debug_traceBlockByHash block ", blockHash, ", error ", err) 653 return data, contracts, err 654 } 655 if len(trace) != len(data) { 656 glog.Error("debug_traceBlockByHash block ", blockHash, ", error: trace length does not match block length ", len(trace), "!=", len(data)) 657 return data, contracts, err 658 } 659 for i, result := range trace { 660 r := &result.Result 661 d := &data[i] 662 if r.Type == "CREATE" || r.Type == "CREATE2" { 663 d.Type = bchain.CREATE 664 d.Contract = r.To 665 contracts = append(contracts, *b.getCreationContractInfo(d.Contract, blockHeight)) 666 } else if r.Type == "SELFDESTRUCT" { 667 d.Type = bchain.SELFDESTRUCT 668 } 669 for j := range r.Calls { 670 contracts = b.processCallTrace(&r.Calls[j], d, contracts, blockHeight) 671 } 672 if r.Error != "" { 673 baseError := PackInternalTransactionError(r.Error) 674 if len(baseError) > 1 { 675 // n, _ := ethNumber(transactions[i].BlockNumber) 676 // glog.Infof("Internal Data Error %d %s: unknown base error %s", n, transactions[i].Hash, baseError) 677 baseError = strings.ToUpper(baseError[:1]) + baseError[1:] + ". " 678 } 679 outputError := ParseErrorFromOutput(r.Output) 680 if len(outputError) > 0 { 681 d.Error = baseError + strings.ToUpper(outputError[:1]) + outputError[1:] 682 } else { 683 traceError := PackInternalTransactionError(d.Error) 684 if traceError == baseError { 685 d.Error = baseError 686 } else { 687 d.Error = baseError + traceError 688 } 689 } 690 // n, _ := ethNumber(transactions[i].BlockNumber) 691 // glog.Infof("Internal Data Error %d %s: %s", n, transactions[i].Hash, UnpackInternalTransactionError([]byte(d.Error))) 692 } 693 } 694 } 695 return data, contracts, nil 696 } 697 698 // GetBlock returns block with given hash or height, hash has precedence if both passed 699 func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) { 700 raw, err := b.getBlockRaw(hash, height, true) 701 if err != nil { 702 return nil, err 703 } 704 var head rpcHeader 705 if err := json.Unmarshal(raw, &head); err != nil { 706 return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) 707 } 708 var body rpcBlockTransactions 709 if err := json.Unmarshal(raw, &body); err != nil { 710 return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) 711 } 712 bbh, err := b.ethHeaderToBlockHeader(&head) 713 if err != nil { 714 return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) 715 } 716 // get block events 717 // TODO - could be possibly done in parallel to getInternalDataForBlock 718 logs, ens, err := b.processEventsForBlock(head.Number) 719 if err != nil { 720 return nil, err 721 } 722 // error fetching internal data does not stop the block processing 723 var blockSpecificData *bchain.EthereumBlockSpecificData 724 internalData, contracts, err := b.getInternalDataForBlock(head.Hash, bbh.Height, body.Transactions) 725 // pass internalData error and ENS records in blockSpecificData to be stored 726 if err != nil || len(ens) > 0 || len(contracts) > 0 { 727 blockSpecificData = &bchain.EthereumBlockSpecificData{} 728 if err != nil { 729 blockSpecificData.InternalDataError = err.Error() 730 // glog.Info("InternalDataError ", bbh.Height, ": ", err.Error()) 731 } 732 if len(ens) > 0 { 733 blockSpecificData.AddressAliasRecords = ens 734 // glog.Info("ENS", ens) 735 } 736 if len(contracts) > 0 { 737 blockSpecificData.Contracts = contracts 738 // glog.Info("Contracts", contracts) 739 } 740 } 741 742 btxs := make([]bchain.Tx, len(body.Transactions)) 743 for i := range body.Transactions { 744 tx := &body.Transactions[i] 745 btx, err := b.Parser.ethTxToTx(tx, &bchain.RpcReceipt{Logs: logs[tx.Hash]}, &internalData[i], bbh.Time, uint32(bbh.Confirmations), true) 746 if err != nil { 747 return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash) 748 } 749 btxs[i] = *btx 750 if b.mempoolInitialized { 751 b.Mempool.RemoveTransactionFromMempool(tx.Hash) 752 } 753 } 754 bbk := bchain.Block{ 755 BlockHeader: *bbh, 756 Txs: btxs, 757 CoinSpecificData: blockSpecificData, 758 } 759 return &bbk, nil 760 } 761 762 // GetBlockInfo returns extended header (more info than in bchain.BlockHeader) with a list of txids 763 func (b *EthereumRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) { 764 raw, err := b.getBlockRaw(hash, 0, false) 765 if err != nil { 766 return nil, err 767 } 768 var head rpcHeader 769 var txs rpcBlockTxids 770 if err := json.Unmarshal(raw, &head); err != nil { 771 return nil, errors.Annotatef(err, "hash %v", hash) 772 } 773 if err = json.Unmarshal(raw, &txs); err != nil { 774 return nil, err 775 } 776 bch, err := b.ethHeaderToBlockHeader(&head) 777 if err != nil { 778 return nil, err 779 } 780 return &bchain.BlockInfo{ 781 BlockHeader: *bch, 782 Difficulty: common.JSONNumber(head.Difficulty), 783 Nonce: common.JSONNumber(head.Nonce), 784 Txids: txs.Transactions, 785 }, nil 786 } 787 788 // GetTransactionForMempool returns a transaction by the transaction ID. 789 // It could be optimized for mempool, i.e. without block time and confirmations 790 func (b *EthereumRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) { 791 return b.GetTransaction(txid) 792 } 793 794 // GetTransaction returns a transaction by the transaction ID. 795 func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { 796 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 797 defer cancel() 798 var tx *bchain.RpcTransaction 799 hash := ethcommon.HexToHash(txid) 800 err := b.RPC.CallContext(ctx, &tx, "eth_getTransactionByHash", hash) 801 if err != nil { 802 return nil, err 803 } else if tx == nil { 804 if b.mempoolInitialized { 805 b.Mempool.RemoveTransactionFromMempool(txid) 806 } 807 return nil, bchain.ErrTxNotFound 808 } 809 var btx *bchain.Tx 810 if tx.BlockNumber == "" { 811 // mempool tx 812 btx, err = b.Parser.ethTxToTx(tx, nil, nil, 0, 0, true) 813 if err != nil { 814 return nil, errors.Annotatef(err, "txid %v", txid) 815 } 816 } else { 817 // non mempool tx - read the block header to get the block time 818 raw, err := b.getBlockRaw(tx.BlockHash, 0, false) 819 if err != nil { 820 return nil, err 821 } 822 var ht struct { 823 Time string `json:"timestamp"` 824 } 825 if err := json.Unmarshal(raw, &ht); err != nil { 826 return nil, errors.Annotatef(err, "hash %v", hash) 827 } 828 var time int64 829 if time, err = ethNumber(ht.Time); err != nil { 830 return nil, errors.Annotatef(err, "txid %v", txid) 831 } 832 var receipt bchain.RpcReceipt 833 err = b.RPC.CallContext(ctx, &receipt, "eth_getTransactionReceipt", hash) 834 if err != nil { 835 return nil, errors.Annotatef(err, "txid %v", txid) 836 } 837 n, err := ethNumber(tx.BlockNumber) 838 if err != nil { 839 return nil, errors.Annotatef(err, "txid %v", txid) 840 } 841 confirmations, err := b.computeConfirmations(uint64(n)) 842 if err != nil { 843 return nil, errors.Annotatef(err, "txid %v", txid) 844 } 845 btx, err = b.Parser.ethTxToTx(tx, &receipt, nil, time, confirmations, true) 846 if err != nil { 847 return nil, errors.Annotatef(err, "txid %v", txid) 848 } 849 // remove tx from mempool if it is there 850 if b.mempoolInitialized { 851 b.Mempool.RemoveTransactionFromMempool(txid) 852 } 853 } 854 return btx, nil 855 } 856 857 // GetTransactionSpecific returns json as returned by backend, with all coin specific data 858 func (b *EthereumRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) { 859 csd, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData) 860 if !ok { 861 ntx, err := b.GetTransaction(tx.Txid) 862 if err != nil { 863 return nil, err 864 } 865 csd, ok = ntx.CoinSpecificData.(bchain.EthereumSpecificData) 866 if !ok { 867 return nil, errors.New("Cannot get CoinSpecificData") 868 } 869 } 870 m, err := json.Marshal(&csd) 871 return json.RawMessage(m), err 872 } 873 874 // GetMempoolTransactions returns transactions in mempool 875 func (b *EthereumRPC) GetMempoolTransactions() ([]string, error) { 876 raw, err := b.getBlockRaw("pending", 0, false) 877 if err != nil { 878 return nil, err 879 } 880 var body rpcBlockTxids 881 if len(raw) > 0 { 882 if err := json.Unmarshal(raw, &body); err != nil { 883 return nil, err 884 } 885 } 886 return body.Transactions, nil 887 } 888 889 // EstimateFee returns fee estimation 890 func (b *EthereumRPC) EstimateFee(blocks int) (big.Int, error) { 891 return b.EstimateSmartFee(blocks, true) 892 } 893 894 // EstimateSmartFee returns fee estimation 895 func (b *EthereumRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) { 896 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 897 defer cancel() 898 var r big.Int 899 gp, err := b.Client.SuggestGasPrice(ctx) 900 if err == nil && b != nil { 901 r = *gp 902 } 903 return r, err 904 } 905 906 // GetStringFromMap attempts to return the value for a specific key in a map as a string if valid, 907 // otherwise returns an empty string with false indicating there was no key found, or the value was not a string 908 func GetStringFromMap(p string, params map[string]interface{}) (string, bool) { 909 v, ok := params[p] 910 if ok { 911 s, ok := v.(string) 912 return s, ok 913 } 914 return "", false 915 } 916 917 // EthereumTypeEstimateGas returns estimation of gas consumption for given transaction parameters 918 func (b *EthereumRPC) EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error) { 919 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 920 defer cancel() 921 msg := ethereum.CallMsg{} 922 if s, ok := GetStringFromMap("from", params); ok && len(s) > 0 { 923 msg.From = ethcommon.HexToAddress(s) 924 } 925 if s, ok := GetStringFromMap("to", params); ok && len(s) > 0 { 926 a := ethcommon.HexToAddress(s) 927 msg.To = &a 928 } 929 if s, ok := GetStringFromMap("data", params); ok && len(s) > 0 { 930 msg.Data = ethcommon.FromHex(s) 931 } 932 if s, ok := GetStringFromMap("value", params); ok && len(s) > 0 { 933 msg.Value, _ = hexutil.DecodeBig(s) 934 } 935 if s, ok := GetStringFromMap("gas", params); ok && len(s) > 0 { 936 msg.Gas, _ = hexutil.DecodeUint64(s) 937 } 938 if s, ok := GetStringFromMap("gasPrice", params); ok && len(s) > 0 { 939 msg.GasPrice, _ = hexutil.DecodeBig(s) 940 } 941 return b.Client.EstimateGas(ctx, msg) 942 } 943 944 // SendRawTransaction sends raw transaction 945 func (b *EthereumRPC) SendRawTransaction(hex string) (string, error) { 946 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 947 defer cancel() 948 var raw json.RawMessage 949 err := b.RPC.CallContext(ctx, &raw, "eth_sendRawTransaction", hex) 950 if err != nil { 951 return "", err 952 } else if len(raw) == 0 { 953 return "", errors.New("SendRawTransaction: failed") 954 } 955 var result string 956 if err := json.Unmarshal(raw, &result); err != nil { 957 return "", errors.Annotatef(err, "raw result %v", raw) 958 } 959 if result == "" { 960 return "", errors.New("SendRawTransaction: failed, empty result") 961 } 962 return result, nil 963 } 964 965 // EthereumTypeGetBalance returns current balance of an address 966 func (b *EthereumRPC) EthereumTypeGetBalance(addrDesc bchain.AddressDescriptor) (*big.Int, error) { 967 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 968 defer cancel() 969 return b.Client.BalanceAt(ctx, addrDesc, nil) 970 } 971 972 // EthereumTypeGetNonce returns current balance of an address 973 func (b *EthereumRPC) EthereumTypeGetNonce(addrDesc bchain.AddressDescriptor) (uint64, error) { 974 ctx, cancel := context.WithTimeout(context.Background(), b.Timeout) 975 defer cancel() 976 return b.Client.NonceAt(ctx, addrDesc, nil) 977 } 978 979 // GetChainParser returns ethereum BlockChainParser 980 func (b *EthereumRPC) GetChainParser() bchain.BlockChainParser { 981 return b.Parser 982 }