github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/rpc/client.go (about) 1 // Copyright 2021 The TrueBlocks Authors. All rights reserved. 2 // Use of this source code is governed by a license that can 3 // be found in the LICENSE file. 4 5 package rpc 6 7 import ( 8 "context" 9 "fmt" 10 "strings" 11 "sync" 12 13 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 14 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" 15 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 16 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc/query" 17 "github.com/ethereum/go-ethereum/ethclient" 18 ) 19 20 // GetClientVersion returns the version of the client 21 func (conn *Connection) GetClientVersion() (version string, err error) { 22 // TODO: C++ code used to cache version info 23 method := "web3_clientVersion" 24 params := query.Params{} 25 26 if version, err := query.Query[string](conn.Chain, method, params); err != nil { 27 return "", err 28 } else { 29 return *version, nil 30 } 31 } 32 33 // GetClientIDs returns both chainId and networkId from the node 34 func (conn *Connection) GetClientIDs() (uint64, uint64, error) { 35 if ec, err := conn.getClient(); err != nil { 36 return 0, 0, err 37 } else { 38 defer ec.Close() 39 40 ch, err := ec.ChainID(context.Background()) 41 if err != nil { 42 return 0, 0, err 43 } 44 45 n, err := ec.NetworkID(context.Background()) 46 if err != nil { 47 return ch.Uint64(), 0, err 48 } 49 50 return ch.Uint64(), n.Uint64(), nil 51 } 52 } 53 54 // GetLatestBlockNumber returns the block number at the front of the chain (i.e. latest) 55 func (conn *Connection) GetLatestBlockNumber() base.Blknum { 56 if ec, err := conn.getClient(); err != nil { 57 logger.Error("Could not connect to RPC client", err) 58 return 0 59 } else { 60 defer ec.Close() 61 62 bn, err := ec.BlockNumber(context.Background()) 63 if err != nil { 64 logger.Error("Could not connect to RPC client", err) 65 return 0 66 } 67 return base.Blknum(bn) 68 } 69 } 70 71 // TODO: this looks weird, but before we were creating and deleting the client with every call which 72 // TODO: overran the number of TPC connection the OS would create (on a Mac). Since then, we now 73 // TODO: open the connection once and just use it allowing the operating system to clean it up 74 var clientMutex sync.Mutex 75 var perProviderClientMap = map[string]*ethclient.Client{} 76 77 func (conn *Connection) getClient() (*ethclient.Client, error) { 78 provider := config.GetChain(conn.Chain).RpcProvider 79 if provider == "" || provider == "https://" { 80 var noProvider = ` 81 82 Warning: The RPC server ([{PROVIDER}]) was not available. Either start it, or edit the rpcProvider 83 value in the file [{FILE}]. Quitting... 84 ` 85 msg := noProvider 86 msg = strings.Replace(msg, "[{PROVIDER}]", provider, -1) 87 msg = strings.Replace(msg, "[{FILE}]", config.PathToConfigFile(), -1) 88 msg = strings.Replace(msg, "https://", "<empty>", -1) 89 return nil, fmt.Errorf("%s", msg) 90 } 91 92 clientMutex.Lock() 93 defer clientMutex.Unlock() 94 95 if perProviderClientMap[provider] == nil { 96 ec, err := ethclient.Dial(provider) 97 if err != nil || ec == nil { 98 logger.Error("Missdial("+provider+"):", err) 99 logger.Fatal("") 100 } 101 perProviderClientMap[provider] = ec 102 } 103 return perProviderClientMap[provider], nil 104 }