Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrapped MultiNode client #1006

Merged
merged 8 commits into from
Jan 15, 2025
Merged
7 changes: 5 additions & 2 deletions pkg/solana/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,9 @@ type chain struct {
lggr logger.Logger

// if multiNode is enabled, the clientCache will not be used
multiNode *mn.MultiNode[mn.StringID, *client.MultiNodeClient]
txSender *mn.TransactionSender[*solanago.Transaction, *client.SendTxResult, mn.StringID, *client.MultiNodeClient]
multiNode *mn.MultiNode[mn.StringID, *client.MultiNodeClient]
txSender *mn.TransactionSender[*solanago.Transaction, *client.SendTxResult, mn.StringID, *client.MultiNodeClient]
multiClient *client.MultiClient

// tracking node chain id for verification
clientCache map[string]*verifiedCachedClient // map URL -> {client, chainId} [mainnet/testnet/devnet/localnet]
Expand Down Expand Up @@ -238,6 +239,8 @@ func newChain(id string, cfg *config.TOMLConfig, ks core.Keystore, lggr logger.L

var tc internal.Loader[client.ReaderWriter] = utils.NewLazyLoad(func() (client.ReaderWriter, error) { return ch.getClient() })
var bc internal.Loader[monitor.BalanceClient] = utils.NewLazyLoad(func() (monitor.BalanceClient, error) { return ch.getClient() })
// getClient returns random client or if MultiNodeEnabled RPC picked and controlled by MultiNode
ch.multiClient = client.NewMultiClient(ch.getClient)

// txm will default to sending transactions using a single RPC client if sendTx is nil
var sendTx func(ctx context.Context, tx *solanago.Transaction) (solanago.Signature, error)
Expand Down
168 changes: 168 additions & 0 deletions pkg/solana/client/multi_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package client

import (
"context"

"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"

mn "github.com/smartcontractkit/chainlink-solana/pkg/solana/client/multinode"
)

var _ ReaderWriter = (*MultiClient)(nil)

// MultiClient - wrapper over multiple RPCs, underlying provider can be MultiNode or LazyLoader.
// Main purpose is to eliminate need for frequent error handling on selection of a client.
type MultiClient struct {
getClient func() (ReaderWriter, error)
}

func NewMultiClient(getClient func() (ReaderWriter, error)) *MultiClient {
return &MultiClient{
getClient: getClient,
}
}

func (m *MultiClient) GetLatestBlockHeight(ctx context.Context) (uint64, error) {
r, err := m.getClient()
if err != nil {
return 0, err
}

return r.GetLatestBlockHeight(ctx)
}

func (m *MultiClient) SendTx(ctx context.Context, tx *solana.Transaction) (solana.Signature, error) {
r, err := m.getClient()
if err != nil {
return solana.Signature{}, err
}

return r.SendTx(ctx, tx)
}

func (m *MultiClient) SimulateTx(ctx context.Context, tx *solana.Transaction, opts *rpc.SimulateTransactionOpts) (*rpc.SimulateTransactionResult, error) {
r, err := m.getClient()
if err != nil {
return nil, err
}

return r.SimulateTx(ctx, tx, opts)
}

func (m *MultiClient) SignatureStatuses(ctx context.Context, sigs []solana.Signature) ([]*rpc.SignatureStatusesResult, error) {
r, err := m.getClient()
if err != nil {
return nil, err
}

return r.SignatureStatuses(ctx, sigs)
}

func (m *MultiClient) GetAccountInfoWithOpts(ctx context.Context, addr solana.PublicKey, opts *rpc.GetAccountInfoOpts) (*rpc.GetAccountInfoResult, error) {
r, err := m.getClient()
if err != nil {
return nil, err
}

return r.GetAccountInfoWithOpts(ctx, addr, opts)
}

func (m *MultiClient) Balance(ctx context.Context, addr solana.PublicKey) (uint64, error) {
r, err := m.getClient()
if err != nil {
return 0, err
}

return r.Balance(ctx, addr)
}

func (m *MultiClient) SlotHeight(ctx context.Context) (uint64, error) {
r, err := m.getClient()
if err != nil {
return 0, err
}

return r.SlotHeight(ctx)
}

func (m *MultiClient) LatestBlockhash(ctx context.Context) (*rpc.GetLatestBlockhashResult, error) {
r, err := m.getClient()
if err != nil {
return nil, err
}

return r.LatestBlockhash(ctx)
}

func (m *MultiClient) ChainID(ctx context.Context) (mn.StringID, error) {
r, err := m.getClient()
if err != nil {
return "", err
}

return r.ChainID(ctx)
}

func (m *MultiClient) GetFeeForMessage(ctx context.Context, msg string) (uint64, error) {
r, err := m.getClient()
if err != nil {
return 0, err
}

return r.GetFeeForMessage(ctx, msg)
}

func (m *MultiClient) GetLatestBlock(ctx context.Context) (*rpc.GetBlockResult, error) {
r, err := m.getClient()
if err != nil {
return nil, err
}

return r.GetLatestBlock(ctx)
}

func (m *MultiClient) GetTransaction(ctx context.Context, txHash solana.Signature, opts *rpc.GetTransactionOpts) (*rpc.GetTransactionResult, error) {
r, err := m.getClient()
if err != nil {
return nil, err
}

return r.GetTransaction(ctx, txHash, opts)
}

func (m *MultiClient) GetBlocks(ctx context.Context, startSlot uint64, endSlot *uint64) (rpc.BlocksResult, error) {
r, err := m.getClient()
if err != nil {
return nil, err
}

return r.GetBlocks(ctx, startSlot, endSlot)
}

func (m *MultiClient) GetBlocksWithLimit(ctx context.Context, startSlot uint64, limit uint64) (*rpc.BlocksResult, error) {
r, err := m.getClient()
if err != nil {
return nil, err
}

return r.GetBlocksWithLimit(ctx, startSlot, limit)
}

func (m *MultiClient) GetBlock(ctx context.Context, slot uint64) (*rpc.GetBlockResult, error) {
r, err := m.getClient()
if err != nil {
return nil, err
}

return r.GetBlock(ctx, slot)
}

func (m *MultiClient) GetSignaturesForAddressWithOpts(ctx context.Context, addr solana.PublicKey, opts *rpc.GetSignaturesForAddressOpts) ([]*rpc.TransactionSignature, error) {
r, err := m.getClient()
if err != nil {
return nil, err
}

return r.GetSignaturesForAddressWithOpts(ctx, addr, opts)
}
Loading