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  }