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  }