Skip to content

Commit 52d8d2a

Browse files
authored
feat: Implemented RPC Manager for RPC calls (#1260)
* 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
1 parent 64e3128 commit 52d8d2a

File tree

143 files changed

+4555
-3771
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

143 files changed

+4555
-3771
lines changed

README.md

+18-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ $ ./razor setConfig --provider <rpc_provider> --gasmultiplier <multiplier_value>
118118
docker
119119

120120
```
121-
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>
121+
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>
122122
```
123123

124124
Example:
@@ -190,6 +190,23 @@ Password:
190190

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

193+
### Import Endpoints
194+
195+
You can import the endpoints to file `$HOME/.razor/endpoints.json` on your local by using the `importEndpoints` command.
196+
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.
197+
198+
razor cli
199+
200+
```
201+
$ ./razor importEndpoints
202+
```
203+
204+
docker
205+
206+
```
207+
docker exec -it razor-go razor importEndpoints
208+
```
209+
193210
### Stake
194211

195212
If you have a minimum of 1000 razors in your account, you can stake those using the addStake command.

accounts/accounts.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"strings"
1414
)
1515

16-
var log = logger.NewLogger()
16+
var log = logger.GetLogger()
1717

1818
//This function takes path and password as input and returns new account
1919
func (am *AccountManager) CreateAccount(keystorePath string, password string) accounts.Account {

block/block.go

+90-18
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,112 @@ package block
22

33
import (
44
"context"
5-
"razor/core"
65
"sync"
76
"time"
87

98
"github.com/ethereum/go-ethereum/core/types"
109
"github.com/ethereum/go-ethereum/ethclient"
1110
"github.com/sirupsen/logrus"
11+
"razor/rpc"
1212
)
1313

14-
var latestBlock *types.Header
15-
var mu = sync.Mutex{}
14+
// BlockMonitor monitors the latest block and handles stale blocks.
15+
type BlockMonitor struct {
16+
client *ethclient.Client
17+
rpcManager *rpc.RPCManager
18+
latestBlock *types.Header
19+
mu sync.Mutex
20+
checkInterval time.Duration
21+
staleThreshold time.Duration
22+
}
23+
24+
// NewBlockMonitor initializes a BlockMonitor with RPC integration.
25+
func NewBlockMonitor(client *ethclient.Client, rpcManager *rpc.RPCManager, checkInterval, staleThreshold time.Duration) *BlockMonitor {
26+
return &BlockMonitor{
27+
client: client,
28+
rpcManager: rpcManager,
29+
checkInterval: time.Second * checkInterval,
30+
staleThreshold: time.Second * staleThreshold,
31+
}
32+
}
1633

17-
func GetLatestBlock() *types.Header {
18-
mu.Lock()
19-
defer mu.Unlock()
20-
return latestBlock
34+
// Start begins the block monitoring process.
35+
func (bm *BlockMonitor) Start() {
36+
go func() {
37+
for {
38+
bm.updateLatestBlock()
39+
bm.checkForStaleBlock()
40+
time.Sleep(bm.checkInterval)
41+
}
42+
}()
2143
}
2244

23-
func SetLatestBlock(block *types.Header) {
24-
mu.Lock()
25-
latestBlock = block
26-
mu.Unlock()
45+
// GetLatestBlock retrieves the most recent block header safely.
46+
func (bm *BlockMonitor) GetLatestBlock() *types.Header {
47+
bm.mu.Lock()
48+
defer bm.mu.Unlock()
49+
return bm.latestBlock
2750
}
2851

29-
func CalculateLatestBlock(client *ethclient.Client) {
30-
for {
31-
if client != nil {
32-
latestHeader, err := client.HeaderByNumber(context.Background(), nil)
52+
// updateLatestBlock fetches the latest block and updates the state.
53+
func (bm *BlockMonitor) updateLatestBlock() {
54+
if bm.client == nil {
55+
return
56+
}
57+
58+
header, err := bm.client.HeaderByNumber(context.Background(), nil)
59+
if err != nil {
60+
logrus.Errorf("Error fetching latest block: %v", err)
61+
return
62+
}
63+
64+
bm.mu.Lock()
65+
defer bm.mu.Unlock()
66+
67+
// Update the latest block only if it changes.
68+
if bm.latestBlock == nil || header.Number.Uint64() != bm.latestBlock.Number.Uint64() {
69+
bm.latestBlock = header
70+
}
71+
}
72+
73+
// checkForStaleBlock detects stale blocks and triggers appropriate actions.
74+
func (bm *BlockMonitor) checkForStaleBlock() {
75+
if bm.staleThreshold == 0 {
76+
return
77+
}
78+
79+
bm.mu.Lock()
80+
defer bm.mu.Unlock()
81+
82+
if bm.latestBlock == nil || time.Since(time.Unix(int64(bm.latestBlock.Time), 0)) >= bm.staleThreshold {
83+
logrus.Warnf("Stale block detected: Block %d is stale for %s", bm.latestBlock.Number.Uint64(), bm.staleThreshold)
84+
85+
// Switch to the next best RPC endpoint if stale block detected.
86+
if bm.rpcManager != nil {
87+
switched, err := bm.rpcManager.SwitchToNextBestRPCClient()
3388
if err != nil {
34-
logrus.Error("CalculateBlockNumber: Error in fetching block: ", err)
89+
logrus.Errorf("Failed to switch RPC endpoint: %v", err)
90+
} else if switched {
91+
logrus.Info("Switched to the next best RPC endpoint.")
92+
bm.updateClient()
3593
} else {
36-
SetLatestBlock(latestHeader)
94+
logrus.Warn("Retaining the current best RPC endpoint as no valid alternate was found.")
3795
}
3896
}
39-
time.Sleep(time.Second * time.Duration(core.BlockNumberInterval))
4097
}
4198
}
99+
100+
// updateClient updates the Ethereum client to use the new best RPC endpoint.
101+
func (bm *BlockMonitor) updateClient() {
102+
if bm.rpcManager == nil {
103+
return
104+
}
105+
106+
newClient, err := bm.rpcManager.GetBestRPCClient()
107+
if err != nil {
108+
return
109+
}
110+
111+
bm.client = newClient
112+
logrus.Info("Client in logger updated with the new best RPC endpoint.")
113+
}

client/alternateClient.go

-63
This file was deleted.

cmd/addStake.go

+23-42
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22
package cmd
33

44
import (
5-
"context"
6-
"razor/accounts"
75
"razor/core"
86
"razor/core/types"
9-
"razor/logger"
107
"razor/pkg/bindings"
8+
"razor/rpc"
119
"razor/utils"
1210

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

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

41-
client := razorUtils.ConnectToClient(config.Provider)
42-
43-
address, err := flagSetUtils.GetStringAddress(flagSet)
44-
utils.CheckError("Error in getting address: ", err)
45-
log.Debug("ExecuteStake: Address: ", address)
46-
47-
logger.SetLoggerParameters(client, address)
48-
log.Debug("Checking to assign log file...")
49-
fileUtils.AssignLogFile(flagSet, config)
50-
51-
log.Debug("Getting password...")
52-
password := razorUtils.AssignPassword(flagSet)
53-
54-
accountManager, err := razorUtils.AccountManagerForKeystore()
55-
utils.CheckError("Error in getting accounts manager for keystore: ", err)
56-
57-
account := accounts.InitAccountStruct(address, password, accountManager)
58-
59-
err = razorUtils.CheckPassword(account)
60-
utils.CheckError("Error in fetching private key from given password: ", err)
61-
62-
balance, err := razorUtils.FetchBalance(client, address)
63-
utils.CheckError("Error in fetching razor balance for account: "+address, err)
38+
balance, err := razorUtils.FetchBalance(rpcParameters, account.Address)
39+
utils.CheckError("Error in fetching razor balance for account: "+account.Address, err)
6440
log.Debug("Getting amount in wei...")
6541
valueInWei, err := cmdUtils.AssignAmountInWei(flagSet)
6642
utils.CheckError("Error in getting amount: ", err)
@@ -70,13 +46,13 @@ func (*UtilsStruct) ExecuteStake(flagSet *pflag.FlagSet) {
7046
razorUtils.CheckAmountAndBalance(valueInWei, balance)
7147

7248
log.Debug("Checking whether sFUEL balance is not 0...")
73-
razorUtils.CheckEthBalanceIsZero(context.Background(), client, address)
49+
razorUtils.CheckEthBalanceIsZero(rpcParameters, account.Address)
7450

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

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

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

8763
if stakerId != 0 {
88-
staker, err := razorUtils.GetStaker(context.Background(), client, stakerId)
64+
staker, err := razorUtils.GetStaker(rpcParameters, stakerId)
8965
utils.CheckError("Error in getting staker: ", err)
9066

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

9672
txnArgs := types.TransactionOptions{
97-
Client: client,
9873
Amount: valueInWei,
9974
ChainId: core.ChainId,
10075
Config: config,
10176
Account: account,
10277
}
10378

10479
log.Debug("ExecuteStake: Calling Approve() for amount: ", txnArgs.Amount)
105-
approveTxnHash, err := cmdUtils.Approve(txnArgs)
80+
approveTxnHash, err := cmdUtils.Approve(rpcParameters, txnArgs)
10681
utils.CheckError("Approve error: ", err)
10782

10883
if approveTxnHash != core.NilHash {
109-
err = razorUtils.WaitForBlockCompletion(txnArgs.Client, approveTxnHash.Hex())
84+
err = razorUtils.WaitForBlockCompletion(rpcParameters, approveTxnHash.Hex())
11085
utils.CheckError("Error in WaitForBlockCompletion for approve: ", err)
11186
}
11287

11388
log.Debug("ExecuteStake: Calling StakeCoins() for amount: ", txnArgs.Amount)
114-
stakeTxnHash, err := cmdUtils.StakeCoins(txnArgs)
89+
stakeTxnHash, err := cmdUtils.StakeCoins(rpcParameters, txnArgs)
11590
utils.CheckError("Stake error: ", err)
11691

117-
err = razorUtils.WaitForBlockCompletion(txnArgs.Client, stakeTxnHash.Hex())
92+
err = razorUtils.WaitForBlockCompletion(rpcParameters, stakeTxnHash.Hex())
11893
utils.CheckError("Error in WaitForBlockCompletion for stake: ", err)
11994
}
12095

12196
//This function allows the user to stake razors in the razor network and returns the hash
122-
func (*UtilsStruct) StakeCoins(txnArgs types.TransactionOptions) (common.Hash, error) {
123-
epoch, err := razorUtils.GetEpoch(context.Background(), txnArgs.Client)
97+
func (*UtilsStruct) StakeCoins(rpcParameters rpc.RPCParameters, txnArgs types.TransactionOptions) (common.Hash, error) {
98+
epoch, err := razorUtils.GetEpoch(rpcParameters)
12499
if err != nil {
125100
return core.NilHash, err
126101
}
@@ -130,9 +105,15 @@ func (*UtilsStruct) StakeCoins(txnArgs types.TransactionOptions) (common.Hash, e
130105
txnArgs.MethodName = "stake"
131106
txnArgs.Parameters = []interface{}{epoch, txnArgs.Amount}
132107
txnArgs.ABI = bindings.StakeManagerMetaData.ABI
133-
txnOpts := razorUtils.GetTxnOpts(context.Background(), txnArgs)
108+
txnOpts := razorUtils.GetTxnOpts(rpcParameters, txnArgs)
109+
110+
client, err := rpcParameters.RPCManager.GetBestRPCClient()
111+
if err != nil {
112+
return core.NilHash, err
113+
}
114+
134115
log.Debugf("Executing Stake transaction with epoch = %d, amount = %d", epoch, txnArgs.Amount)
135-
txn, err := stakeManagerUtils.Stake(txnArgs.Client, txnOpts, epoch, txnArgs.Amount)
116+
txn, err := stakeManagerUtils.Stake(client, txnOpts, epoch, txnArgs.Amount)
136117
if err != nil {
137118
return core.NilHash, err
138119
}

0 commit comments

Comments
 (0)