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