package client import ( "sync" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/rpc/client" "github.com/nspcc-dev/neo-go/pkg/wallet" "go.uber.org/zap" ) type multiClient struct { cfg cfg account *wallet.Account sharedNotary *notary // notary config needed for single client construction endpoints []string clientsMtx sync.Mutex clients map[string]*Client } // createForAddress creates single Client instance using provided endpoint. // // note: must be wrapped into mutex lock. func (x *multiClient) createForAddress(addr string) (*Client, error) { cli, err := client.New(x.cfg.ctx, addr, client.Options{ DialTimeout: x.cfg.dialTimeout, }) if err != nil { return nil, err } err = cli.Init() // magic number is set there based on RPC node answer if err != nil { return nil, err } gas, err := cli.GetNativeContractHash(nativenames.Gas) if err != nil { return nil, err } c := &Client{ singleClient: &singleClient{ logger: x.cfg.logger, client: cli, acc: x.account, gas: gas, waitInterval: x.cfg.waitInterval, signer: x.cfg.signer, notary: x.sharedNotary, }, } x.clients[addr] = c return c, nil } func (x *multiClient) iterateClients(f func(*Client) error) error { var ( firstErr error err error ) for i := range x.endpoints { select { case <-x.cfg.ctx.Done(): return x.cfg.ctx.Err() default: } x.clientsMtx.Lock() c, cached := x.clients[x.endpoints[i]] if !cached { c, err = x.createForAddress(x.endpoints[i]) } x.clientsMtx.Unlock() if !cached && err != nil { x.cfg.logger.Error("could not open morph client connection", zap.String("endpoint", x.endpoints[i]), zap.String("err", err.Error()), ) } else { err = f(c) } success := err == nil if success || firstErr == nil { firstErr = err if success { break } } } return firstErr }