github.com/stafiprotocol/go-substrate-rpc-client@v1.4.7/client/gsrpc_client.go (about)

     1  package client
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"sync"
     8  	"time"
     9  
    10  	scale "github.com/itering/scale.go"
    11  	"github.com/itering/scale.go/source"
    12  	scaleTypes "github.com/itering/scale.go/types"
    13  	"github.com/itering/scale.go/utiles"
    14  	"github.com/itering/substrate-api-rpc/rpc"
    15  	gsrpcConfig "github.com/stafiprotocol/go-substrate-rpc-client/config"
    16  	"github.com/stafiprotocol/go-substrate-rpc-client/pkg/recws"
    17  	"github.com/stafiprotocol/go-substrate-rpc-client/pkg/stafidecoder"
    18  	"github.com/stafiprotocol/go-substrate-rpc-client/pkg/websocketpool"
    19  	gsrpc "github.com/stafiprotocol/go-substrate-rpc-client/rpc"
    20  	"github.com/stafiprotocol/go-substrate-rpc-client/signature"
    21  	"github.com/stafiprotocol/go-substrate-rpc-client/types"
    22  )
    23  
    24  const (
    25  	wsId       = 1
    26  	storageKey = "0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"
    27  )
    28  
    29  const (
    30  	ChainTypeStafi    = "stafi"
    31  	ChainTypePolkadot = "polkadot"
    32  
    33  	AddressTypeAccountId    = "AccountId"
    34  	AddressTypeMultiAddress = "MultiAddress"
    35  )
    36  
    37  var (
    38  	ErrorTerminated           = errors.New("terminated")
    39  	ErrorBondEqualToUnbond    = errors.New("ErrorBondEqualToUnbond")
    40  	ErrorDiffSmallerThanLeast = errors.New("ErrorDiffSmallerThanLeast")
    41  	ErrorValueNotExist        = errors.New("value not exist")
    42  )
    43  
    44  type GsrpcClient struct {
    45  	endpoint    string
    46  	addressType string
    47  	rpcs        *gsrpc.RPCS
    48  	key         *signature.KeyringPair
    49  	genesisHash types.Hash
    50  
    51  	wsPool    websocket_pool.Pool
    52  	log       Logger
    53  	chainType string
    54  	typesPath string
    55  
    56  	currentSpecVersion int
    57  
    58  	stafiMetaDecoderMap map[int]*stafi_decoder.MetadataDecoder
    59  	polkaMetaDecoderMap map[int]*scale.MetadataDecoder
    60  	sync.RWMutex
    61  
    62  	metaDataVersion int
    63  }
    64  
    65  func NewGsrpcClient(chainType, endpoint, typesPath, addressType string, key *signature.KeyringPair, log Logger) (*GsrpcClient, error) {
    66  	log.Info("Connecting to substrate chain with gsrpc client", "endpoint", endpoint)
    67  
    68  	if addressType != AddressTypeAccountId && addressType != AddressTypeMultiAddress {
    69  		return nil, errors.New("addressType not supported")
    70  	}
    71  
    72  	rpcs, err := gsrpc.NewRPCS(endpoint)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	gsrpcConfig.SetSubscribeTimeout(2 * time.Minute)
    78  	latestHash, err := rpcs.Chain.GetFinalizedHead()
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	log.Info("NewGsrpcClient", "latestHash", latestHash.Hex())
    83  
    84  	genesisHash, err := rpcs.Chain.GetBlockHash(0)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	sc := &GsrpcClient{
    90  		endpoint:            endpoint,
    91  		chainType:           chainType,
    92  		addressType:         addressType,
    93  		rpcs:                rpcs,
    94  		key:                 key,
    95  		genesisHash:         genesisHash,
    96  		wsPool:              nil,
    97  		log:                 log,
    98  		typesPath:           typesPath,
    99  		currentSpecVersion:  -1,
   100  		stafiMetaDecoderMap: make(map[int]*stafi_decoder.MetadataDecoder),
   101  		polkaMetaDecoderMap: make(map[int]*scale.MetadataDecoder),
   102  	}
   103  
   104  	err = sc.regCustomTypes()
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	switch chainType {
   110  	case ChainTypeStafi:
   111  		_, err := sc.getStafiMetaDecoder(latestHash.Hex())
   112  		if err != nil {
   113  			return nil, err
   114  		}
   115  	case ChainTypePolkadot:
   116  		_, err := sc.getPolkaMetaDecoder(latestHash.Hex())
   117  		if err != nil {
   118  			return nil, err
   119  		}
   120  	default:
   121  		return nil, fmt.Errorf("chain type not support: %s", chainType)
   122  	}
   123  
   124  	return sc, nil
   125  }
   126  
   127  func (s *GsrpcClient) getStafiMetaDecoder(blockHash string) (*stafi_decoder.MetadataDecoder, error) {
   128  	v := &rpc.JsonRpcResult{}
   129  	// runtime version
   130  	if err := s.sendWsRequest(v, rpc.ChainGetRuntimeVersion(wsId, blockHash)); err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	r := v.ToRuntimeVersion()
   135  	if r == nil {
   136  		return nil, fmt.Errorf("runtime version nil")
   137  	}
   138  	s.RLock()
   139  	if decoder, exist := s.stafiMetaDecoderMap[r.SpecVersion]; exist {
   140  		s.RUnlock()
   141  		return decoder, nil
   142  	}
   143  	s.RUnlock()
   144  
   145  	// check metadata need update, maybe  get ahead hash
   146  	if err := s.sendWsRequest(v, rpc.StateGetMetadata(wsId, blockHash)); err != nil {
   147  		return nil, err
   148  	}
   149  	metaRaw, err := v.ToString()
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  
   154  	md := &stafi_decoder.MetadataDecoder{}
   155  	md.Init(utiles.HexToBytes(metaRaw))
   156  	if err := md.Process(); err != nil {
   157  		return nil, err
   158  	}
   159  	s.Lock()
   160  	s.stafiMetaDecoderMap[r.SpecVersion] = md
   161  	s.Unlock()
   162  
   163  	if r.SpecVersion > s.currentSpecVersion {
   164  		s.currentSpecVersion = r.SpecVersion
   165  		s.metaDataVersion = md.Metadata.MetadataVersion
   166  	}
   167  	return md, nil
   168  
   169  }
   170  
   171  func (s *GsrpcClient) getPolkaMetaDecoder(blockHash string) (*scale.MetadataDecoder, error) {
   172  	v := &rpc.JsonRpcResult{}
   173  	// runtime version
   174  	if err := s.sendWsRequest(v, rpc.ChainGetRuntimeVersion(wsId, blockHash)); err != nil {
   175  		return nil, err
   176  	}
   177  
   178  	r := v.ToRuntimeVersion()
   179  	if r == nil {
   180  		return nil, fmt.Errorf("runtime version nil")
   181  	}
   182  	s.RLock()
   183  	if decoder, exist := s.polkaMetaDecoderMap[r.SpecVersion]; exist {
   184  		s.RUnlock()
   185  		return decoder, nil
   186  	}
   187  	s.RUnlock()
   188  
   189  	// check metadata need update, maybe  get ahead hash
   190  	if err := s.sendWsRequest(v, rpc.StateGetMetadata(wsId, blockHash)); err != nil {
   191  		return nil, err
   192  	}
   193  	metaRaw, err := v.ToString()
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	md := scale.MetadataDecoder{}
   199  	md.Init(utiles.HexToBytes(metaRaw))
   200  	if err := md.Process(); err != nil {
   201  		return nil, err
   202  	}
   203  	s.Lock()
   204  	s.polkaMetaDecoderMap[r.SpecVersion] = &md
   205  	s.Unlock()
   206  
   207  	if r.SpecVersion > s.currentSpecVersion {
   208  		s.currentSpecVersion = r.SpecVersion
   209  		s.metaDataVersion = md.Metadata.MetadataVersion
   210  	}
   211  
   212  	return &md, nil
   213  
   214  }
   215  
   216  func (sc *GsrpcClient) regCustomTypes() error {
   217  	content := []byte(stafi_decoder.DefaultStafiCustumTypes)
   218  	var err error
   219  	if len(sc.typesPath) != 0 {
   220  		content, err = os.ReadFile(sc.typesPath)
   221  		if err != nil {
   222  			return err
   223  		}
   224  	}
   225  
   226  	switch sc.chainType {
   227  	case ChainTypeStafi:
   228  		stafi_decoder.RuntimeType{}.Reg()
   229  		stafi_decoder.RegCustomTypes(stafi_decoder.LoadTypeRegistry(content))
   230  	case ChainTypePolkadot:
   231  		scaleTypes.RegCustomTypes(source.LoadTypeRegistry(content))
   232  	default:
   233  		return fmt.Errorf("chainType not supported")
   234  	}
   235  	return nil
   236  }
   237  
   238  func (sc *GsrpcClient) initial() (*websocket_pool.PoolConn, error) {
   239  	var err error
   240  	if sc.wsPool == nil {
   241  		factory := func() (*websocket_pool.PoolConn, error) {
   242  			SubscribeConn := &recws.RecConn{KeepAliveTimeout: 2 * time.Minute}
   243  			SubscribeConn.Dial(sc.endpoint, nil)
   244  			sc.log.Debug("conn factory create new conn", "endpoint", sc.endpoint)
   245  			return websocket_pool.WrapConn(SubscribeConn), err
   246  		}
   247  		if sc.wsPool, err = websocket_pool.NewWsPool(1, 100, factory); err != nil {
   248  			sc.log.Error("wbskt.NewChannelPool", "err", err)
   249  			return nil, err
   250  		}
   251  	}
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  	conn, err := sc.wsPool.Get()
   256  	return conn, err
   257  }