github.com/0chain/gosdk@v1.17.11/zmagmacore/http/sc-api.go (about) 1 package http 2 3 import ( 4 "crypto/sha1" 5 "encoding/hex" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net/http" 10 "net/url" 11 12 "github.com/0chain/gosdk/core/util" 13 "github.com/0chain/gosdk/zcncore" 14 "github.com/0chain/gosdk/zmagmacore/errors" 15 ) 16 17 // MakeSCRestAPICall calls smart contract with provided address 18 // and makes retryable request to smart contract resource with provided relative path using params. 19 func MakeSCRestAPICall(scAddress string, relativePath string, params map[string]string) ([]byte, error) { 20 var ( 21 resMaxCounterBody []byte 22 23 hashMaxCounter int 24 hashCounters = make(map[string]int) 25 26 sharders = extractSharders() 27 28 lastErrMsg string 29 ) 30 31 for _, sharder := range sharders { 32 var ( 33 client = NewRetryableClient(5) 34 u = makeScURL(params, sharder, scAddress, relativePath) 35 ) 36 37 resp, err := client.Get(u.String()) 38 if err != nil { 39 lastErrMsg = fmt.Sprintf("error while requesting sharders: %v", err) 40 continue 41 } 42 hash, resBody, err := hashAndBytesOfReader(resp.Body) 43 _ = resp.Body.Close() 44 if err != nil { 45 lastErrMsg = fmt.Sprintf("error while reading response body: %v", err) 46 continue 47 } 48 if resp.StatusCode != http.StatusOK { 49 lastErrMsg = fmt.Sprintf("response status is not OK; response body: %s", string(resBody)) 50 continue 51 } 52 53 hashCounters[hash]++ 54 if hashCounters[hash] > hashMaxCounter { 55 hashMaxCounter = hashCounters[hash] 56 resMaxCounterBody = resBody 57 } 58 } 59 60 if hashMaxCounter == 0 { 61 return nil, errors.New("request_sharders", "no valid responses, last err: "+lastErrMsg) 62 } 63 64 return resMaxCounterBody, nil 65 } 66 67 // hashAndBytesOfReader computes hash of readers data and returns hash encoded to hex and bytes of reader data. 68 // If error occurs while reading data from reader, it returns non nil error. 69 func hashAndBytesOfReader(r io.Reader) (hash string, reader []byte, err error) { 70 h := sha1.New() 71 teeReader := io.TeeReader(r, h) 72 readerBytes, err := ioutil.ReadAll(teeReader) 73 if err != nil { 74 return "", nil, err 75 } 76 77 return hex.EncodeToString(h.Sum(nil)), readerBytes, nil 78 } 79 80 // extractSharders returns string slice of randomly ordered sharders existing in the current network. 81 func extractSharders() []string { 82 network := zcncore.GetNetwork() 83 return util.GetRandom(network.Sharders, len(network.Sharders)) 84 } 85 86 const ( 87 // ScRestApiUrl represents base URL path to execute smart contract rest points. 88 ScRestApiUrl = "v1/screst/" 89 ) 90 91 // makeScURL creates url.URL to make smart contract request to sharder. 92 func makeScURL(params map[string]string, sharder, scAddress, relativePath string) *url.URL { 93 uString := fmt.Sprintf("%v/%v%v%v", sharder, ScRestApiUrl, scAddress, relativePath) 94 u, _ := url.Parse(uString) 95 q := u.Query() 96 for k, v := range params { 97 q.Add(k, v) 98 } 99 u.RawQuery = q.Encode() 100 101 return u 102 }