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 }