Skip to content

Commit

Permalink
feat: Implemented RPC Manager for RPC calls (#1260)
Browse files Browse the repository at this point in the history
* feature: introduced RPC manager module

* feat: Implemented RPCParameters for all contract calls

* refactor: moved RPCParameters struct to RPC module

* fix: added assetId parameter to getActiveStatus retry call

* refactor: passed rpcParameters to Batch call functions instead of client

* fix: path to assets.json and client parameter index fixed

* refactor: tests/updated mocks

* refactor: revert mainnet addresses change

* fix: added ctx parameter in CheckTransactionReceipt

* fix: revert chain Id and contract addresses change

* fix: refreshed RPC list after confirm state every epoch

* fix: added disputes contract call to the blockManager struct

* refactor: fixed lint log error

* reafctor: fixed tests

* fix: calculated dynamically the path to endpoints.json file

* fix: endpoints.json file to be picked from .razor directory instaed of repo

* refactor: set up temp endpoints.json file for tests

* feat: added importEndpoints command

* refactor: removed alternateProvider functions and flag as its not required now

* fix: switch to next client only if the connection is successful

* feat: added state block check by introducing blockMonitor (#1262)

* feat: introduced block monitor to keep track of blocks

* refactor: removed global variables used in logger and added blockMonitor as a field

* refactor: used refactored logger module

* fix: fixed logger global instance

* refactor: exported logrus instance field from logger struct

* refactor: fixed tests

* refactor: removed  unwanted return variables in tests

* refactor: added log for current best endpoint URL after every refresh

* fix: added endpoint validation while switching endpoints

* fix: added switched returned type while switching endpoints and removed staleBlockCallback field

* refactor: used BestRPCClient from BestEndpoint

* refactor: renamed RPC module to rpc

* refactor: removed unwanted logs

* refactor: corrected importEndpoints command info
  • Loading branch information
Yashk767 authored Dec 13, 2024
1 parent 64e3128 commit 52d8d2a
Show file tree
Hide file tree
Showing 143 changed files with 4,555 additions and 3,771 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ $ ./razor setConfig --provider <rpc_provider> --gasmultiplier <multiplier_value>
docker

```
docker exec -it razor-go razor setConfig --provider <rpc_provider> --alternateProvider <alternate_rpc_provider> --gasmultiplier <multiplier_value> --buffer <buffer_percentage> --wait <wait_for_n_blocks> --gasprice <gas_price> --logLevel <debug_or_info> --gasLimit <gas_limit_multiplier> --rpcTimeout <rpc_timeout> --httpTimeout <http_timeout> --logFileMaxSize <file_max_size> --logFileMaxBackups <file_max_backups> --logFileMaxAge <file_max_age>
docker exec -it razor-go razor setConfig --provider <rpc_provider> --gasmultiplier <multiplier_value> --buffer <buffer_percentage> --wait <wait_for_n_blocks> --gasprice <gas_price> --logLevel <debug_or_info> --gasLimit <gas_limit_multiplier> --rpcTimeout <rpc_timeout> --httpTimeout <http_timeout> --logFileMaxSize <file_max_size> --logFileMaxBackups <file_max_backups> --logFileMaxAge <file_max_age>
```

Example:
Expand Down Expand Up @@ -190,6 +190,23 @@ Password:

_Before staking on Razor Network, please ensure your account has sFUEL and RAZOR. For testnet RAZOR, please contact us on Discord._

### Import Endpoints

You can import the endpoints to file `$HOME/.razor/endpoints.json` on your local by using the `importEndpoints` command.
This command imports multiple providers along with the user input provider, which are then sorted according to the best performance. The best provider is thus chosen by the RPC manager and will be used to make the RPC calls.

razor cli

```
$ ./razor importEndpoints
```

docker

```
docker exec -it razor-go razor importEndpoints
```

### Stake

If you have a minimum of 1000 razors in your account, you can stake those using the addStake command.
Expand Down
2 changes: 1 addition & 1 deletion accounts/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"strings"
)

var log = logger.NewLogger()
var log = logger.GetLogger()

//This function takes path and password as input and returns new account
func (am *AccountManager) CreateAccount(keystorePath string, password string) accounts.Account {
Expand Down
108 changes: 90 additions & 18 deletions block/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,112 @@ package block

import (
"context"
"razor/core"
"sync"
"time"

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/sirupsen/logrus"
"razor/rpc"
)

var latestBlock *types.Header
var mu = sync.Mutex{}
// BlockMonitor monitors the latest block and handles stale blocks.
type BlockMonitor struct {
client *ethclient.Client
rpcManager *rpc.RPCManager
latestBlock *types.Header
mu sync.Mutex
checkInterval time.Duration
staleThreshold time.Duration
}

// NewBlockMonitor initializes a BlockMonitor with RPC integration.
func NewBlockMonitor(client *ethclient.Client, rpcManager *rpc.RPCManager, checkInterval, staleThreshold time.Duration) *BlockMonitor {
return &BlockMonitor{
client: client,
rpcManager: rpcManager,
checkInterval: time.Second * checkInterval,
staleThreshold: time.Second * staleThreshold,
}
}

func GetLatestBlock() *types.Header {
mu.Lock()
defer mu.Unlock()
return latestBlock
// Start begins the block monitoring process.
func (bm *BlockMonitor) Start() {
go func() {
for {
bm.updateLatestBlock()
bm.checkForStaleBlock()
time.Sleep(bm.checkInterval)
}
}()
}

func SetLatestBlock(block *types.Header) {
mu.Lock()
latestBlock = block
mu.Unlock()
// GetLatestBlock retrieves the most recent block header safely.
func (bm *BlockMonitor) GetLatestBlock() *types.Header {
bm.mu.Lock()
defer bm.mu.Unlock()
return bm.latestBlock
}

func CalculateLatestBlock(client *ethclient.Client) {
for {
if client != nil {
latestHeader, err := client.HeaderByNumber(context.Background(), nil)
// updateLatestBlock fetches the latest block and updates the state.
func (bm *BlockMonitor) updateLatestBlock() {
if bm.client == nil {
return
}

header, err := bm.client.HeaderByNumber(context.Background(), nil)
if err != nil {
logrus.Errorf("Error fetching latest block: %v", err)
return
}

bm.mu.Lock()
defer bm.mu.Unlock()

// Update the latest block only if it changes.
if bm.latestBlock == nil || header.Number.Uint64() != bm.latestBlock.Number.Uint64() {
bm.latestBlock = header
}
}

// checkForStaleBlock detects stale blocks and triggers appropriate actions.
func (bm *BlockMonitor) checkForStaleBlock() {
if bm.staleThreshold == 0 {
return
}

bm.mu.Lock()
defer bm.mu.Unlock()

if bm.latestBlock == nil || time.Since(time.Unix(int64(bm.latestBlock.Time), 0)) >= bm.staleThreshold {
logrus.Warnf("Stale block detected: Block %d is stale for %s", bm.latestBlock.Number.Uint64(), bm.staleThreshold)

// Switch to the next best RPC endpoint if stale block detected.
if bm.rpcManager != nil {
switched, err := bm.rpcManager.SwitchToNextBestRPCClient()
if err != nil {
logrus.Error("CalculateBlockNumber: Error in fetching block: ", err)
logrus.Errorf("Failed to switch RPC endpoint: %v", err)
} else if switched {
logrus.Info("Switched to the next best RPC endpoint.")
bm.updateClient()
} else {
SetLatestBlock(latestHeader)
logrus.Warn("Retaining the current best RPC endpoint as no valid alternate was found.")
}
}
time.Sleep(time.Second * time.Duration(core.BlockNumberInterval))
}
}

// updateClient updates the Ethereum client to use the new best RPC endpoint.
func (bm *BlockMonitor) updateClient() {
if bm.rpcManager == nil {
return
}

newClient, err := bm.rpcManager.GetBestRPCClient()
if err != nil {
return
}

bm.client = newClient
logrus.Info("Client in logger updated with the new best RPC endpoint.")
}
63 changes: 0 additions & 63 deletions client/alternateClient.go

This file was deleted.

65 changes: 23 additions & 42 deletions cmd/addStake.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
package cmd

import (
"context"
"razor/accounts"
"razor/core"
"razor/core/types"
"razor/logger"
"razor/pkg/bindings"
"razor/rpc"
"razor/utils"

"github.com/spf13/pflag"
Expand All @@ -34,33 +32,11 @@ func initialiseStake(cmd *cobra.Command, args []string) {

//This function sets the flags appropriately and executes the StakeCoins function
func (*UtilsStruct) ExecuteStake(flagSet *pflag.FlagSet) {
config, err := cmdUtils.GetConfigData()
utils.CheckError("Error in getting config: ", err)
log.Debugf("ExecuteStake: config: %+v", config)
config, rpcParameters, account, err := InitializeCommandDependencies(flagSet)
utils.CheckError("Error in initialising command dependencies: ", err)

client := razorUtils.ConnectToClient(config.Provider)

address, err := flagSetUtils.GetStringAddress(flagSet)
utils.CheckError("Error in getting address: ", err)
log.Debug("ExecuteStake: Address: ", address)

logger.SetLoggerParameters(client, address)
log.Debug("Checking to assign log file...")
fileUtils.AssignLogFile(flagSet, config)

log.Debug("Getting password...")
password := razorUtils.AssignPassword(flagSet)

accountManager, err := razorUtils.AccountManagerForKeystore()
utils.CheckError("Error in getting accounts manager for keystore: ", err)

account := accounts.InitAccountStruct(address, password, accountManager)

err = razorUtils.CheckPassword(account)
utils.CheckError("Error in fetching private key from given password: ", err)

balance, err := razorUtils.FetchBalance(client, address)
utils.CheckError("Error in fetching razor balance for account: "+address, err)
balance, err := razorUtils.FetchBalance(rpcParameters, account.Address)
utils.CheckError("Error in fetching razor balance for account: "+account.Address, err)
log.Debug("Getting amount in wei...")
valueInWei, err := cmdUtils.AssignAmountInWei(flagSet)
utils.CheckError("Error in getting amount: ", err)
Expand All @@ -70,13 +46,13 @@ func (*UtilsStruct) ExecuteStake(flagSet *pflag.FlagSet) {
razorUtils.CheckAmountAndBalance(valueInWei, balance)

log.Debug("Checking whether sFUEL balance is not 0...")
razorUtils.CheckEthBalanceIsZero(context.Background(), client, address)
razorUtils.CheckEthBalanceIsZero(rpcParameters, account.Address)

minSafeRazor, err := razorUtils.GetMinSafeRazor(context.Background(), client)
minSafeRazor, err := razorUtils.GetMinSafeRazor(rpcParameters)
utils.CheckError("Error in getting minimum safe razor amount: ", err)
log.Debug("ExecuteStake: Minimum razor that you can stake for first time: ", minSafeRazor)

stakerId, err := razorUtils.GetStakerId(context.Background(), client, address)
stakerId, err := razorUtils.GetStakerId(rpcParameters, account.Address)
utils.CheckError("Error in getting stakerId: ", err)
log.Debug("ExecuteStake: Staker Id: ", stakerId)

Expand All @@ -85,7 +61,7 @@ func (*UtilsStruct) ExecuteStake(flagSet *pflag.FlagSet) {
}

if stakerId != 0 {
staker, err := razorUtils.GetStaker(context.Background(), client, stakerId)
staker, err := razorUtils.GetStaker(rpcParameters, stakerId)
utils.CheckError("Error in getting staker: ", err)

if staker.IsSlashed {
Expand All @@ -94,33 +70,32 @@ func (*UtilsStruct) ExecuteStake(flagSet *pflag.FlagSet) {
}

txnArgs := types.TransactionOptions{
Client: client,
Amount: valueInWei,
ChainId: core.ChainId,
Config: config,
Account: account,
}

log.Debug("ExecuteStake: Calling Approve() for amount: ", txnArgs.Amount)
approveTxnHash, err := cmdUtils.Approve(txnArgs)
approveTxnHash, err := cmdUtils.Approve(rpcParameters, txnArgs)
utils.CheckError("Approve error: ", err)

if approveTxnHash != core.NilHash {
err = razorUtils.WaitForBlockCompletion(txnArgs.Client, approveTxnHash.Hex())
err = razorUtils.WaitForBlockCompletion(rpcParameters, approveTxnHash.Hex())
utils.CheckError("Error in WaitForBlockCompletion for approve: ", err)
}

log.Debug("ExecuteStake: Calling StakeCoins() for amount: ", txnArgs.Amount)
stakeTxnHash, err := cmdUtils.StakeCoins(txnArgs)
stakeTxnHash, err := cmdUtils.StakeCoins(rpcParameters, txnArgs)
utils.CheckError("Stake error: ", err)

err = razorUtils.WaitForBlockCompletion(txnArgs.Client, stakeTxnHash.Hex())
err = razorUtils.WaitForBlockCompletion(rpcParameters, stakeTxnHash.Hex())
utils.CheckError("Error in WaitForBlockCompletion for stake: ", err)
}

//This function allows the user to stake razors in the razor network and returns the hash
func (*UtilsStruct) StakeCoins(txnArgs types.TransactionOptions) (common.Hash, error) {
epoch, err := razorUtils.GetEpoch(context.Background(), txnArgs.Client)
func (*UtilsStruct) StakeCoins(rpcParameters rpc.RPCParameters, txnArgs types.TransactionOptions) (common.Hash, error) {
epoch, err := razorUtils.GetEpoch(rpcParameters)
if err != nil {
return core.NilHash, err
}
Expand All @@ -130,9 +105,15 @@ func (*UtilsStruct) StakeCoins(txnArgs types.TransactionOptions) (common.Hash, e
txnArgs.MethodName = "stake"
txnArgs.Parameters = []interface{}{epoch, txnArgs.Amount}
txnArgs.ABI = bindings.StakeManagerMetaData.ABI
txnOpts := razorUtils.GetTxnOpts(context.Background(), txnArgs)
txnOpts := razorUtils.GetTxnOpts(rpcParameters, txnArgs)

client, err := rpcParameters.RPCManager.GetBestRPCClient()
if err != nil {
return core.NilHash, err
}

log.Debugf("Executing Stake transaction with epoch = %d, amount = %d", epoch, txnArgs.Amount)
txn, err := stakeManagerUtils.Stake(txnArgs.Client, txnOpts, epoch, txnArgs.Amount)
txn, err := stakeManagerUtils.Stake(client, txnOpts, epoch, txnArgs.Amount)
if err != nil {
return core.NilHash, err
}
Expand Down
Loading

0 comments on commit 52d8d2a

Please sign in to comment.