Need some help on how to implement an restfull api app based on golang
Question: My coding skills are a bit low :) Recently i started learning golang and how
to handle an Api communication app. Have been having a great time learning it
by myself, golang is revealing itself as a challenging language with great
rewards in the end (code sense ^^).

Have been trying to create a cryptsy api lib for golang based on their API V2
(BETA) which is a restfull api. They have a python lib on their api website
<https://github.com/ScriptProdigy/CryptsyPythonV2/blob/master/Cryptsy.py>.

So far have been able to get the public access working but am having a really
hard time at the private access because of the authentication part.. I find
that the info they give on their website on how to implement it is a bit
confusing :(

> Authorization is performed by sending the following variables into the
> request header Key
>
>   * Public API key.
>   * All query data (nonce=blahblah&limit=blahblah) signed by a secret key
> according to HMAC-SHA512 method. Your secret key and public keys can be
> generated from your account settings page. Every request requires a unique
> nonce. (Suggested to use unix timestamp with microseconds)
>

For this authentication part the python code goes as:

    
    
    def _query(self, method, id=None, action=None, query=[], get_method="GET"):
        query.append(('nonce', time.time()))
        queryStr = urllib.urlencode(query)
    
        link = 'https://' + self.domain + route
    
        sign = hmac.new(self.PrivateKey.encode('utf-8'), queryStr, hashlib.sha512).hexdigest()
    
        headers = {'Sign': sign, 'Key': self.PublicKey.encode('utf-8')}
    

Got this far in golang:

    
    
    package main
    
    import(
    
        "crypto/hmac"
        "crypto/sha512"
        "encoding/hex"
        "encoding/json"
        "errors"
        "fmt"
        "io/ioutil"
        "net/http"
        "strings"
        "time"
    
    )
    
    const (
    
        API_BASE_CRY    = "https://api.cryptsy.com/api/"
        API_VERSION_CRY = "v2"
        API_KEY_CRY     = "xxxxx"
        API_SECRET_CRY  = "xxxxxxxxxxxx"
        DEFAULT_HTTPCLIENT_TIMEOUT = 30 // HTTP client timeout
    )
    
    type clientCry struct {
        apiKey     string
        apiSecret  string
        httpClient *http.Client
    }
    
    type Cryptsy struct {
        clientCry *clientCry
    }
    
    type CryptsyApiRsp struct {
        Success bool            `json:"success"`
        Data    json.RawMessage `json:"data"`
    }
    
    func NewCry(apiKey, apiSecret string) *Cryptsy {
        clientCry := NewClientCry(apiKey, apiSecret)
        return &Cryptsy{clientCry}
    }
    
    func NewClientCry(apiKey, apiSecret string) (c *clientCry) {
        return &clientCry{apiKey, apiSecret, &http.Client{}}
    }
    
    func ComputeHmac512Hex(secret, payload string) string {
        h := hmac.New(sha512.New, []byte(secret))
        h.Write([]byte(payload))
        return hex.EncodeToString(h.Sum(nil))
    }
    
    func (c *clientCry) doTimeoutRequestCry(timer *time.Timer, req *http.Request) (*http.Response, error) {
    
        type data struct {
           resp *http.Response
           err  error
        }
    
        done := make(chan data, 1)
        go func() {
           resp, err := c.httpClient.Do(req)
           done <- data{resp, err}
        }()
    
        select {
           case r := <-done:
           return r.resp, r.err
           case <-timer.C:
              return nil, errors.New("timeout on reading data from Bittrex API")
        }
    }
    
    func (c *clientCry) doCry(method string, ressource string, payload string, authNeeded bool) (response []byte, err error) {
        connectTimer := time.NewTimer(DEFAULT_HTTPCLIENT_TIMEOUT * time.Second)
    
        var rawurl string        
    
        nonce := time.Now().UnixNano()
        result :=  fmt.Sprintf("nonce=%d", nonce)
        rawurl = fmt.Sprintf("%s%s/%s?%s", API_BASE_CRY ,API_VERSION_CRY , ressource, result )
        req, err := http.NewRequest(method, rawurl, strings.NewReader(payload))
    
        sig := ComputeHmac512Hex(API_SECRET_CRY, result)
    
        req.Header.Add("Sign", sig)
        req.Header.Add("Key", API_KEY_CRY )
    
        resp, err := c.doTimeoutRequestCry(connectTimer, req)
        defer resp.Body.Close()
    
        response, err = ioutil.ReadAll(resp.Body)
        fmt.Println(fmt.Sprintf("reponse %s", response), err)
        return response, err
    }
    
    func main() {
    
        crypsy := NewCry(API_KEY_CRY, API_SECRET_CRY)
        r, _ := crypsy.clientCry.doCry("GET", "info", "", true) 
        fmt.Println(r)
    }
    

and my output is :

`response {"success":false,"error":["Must be authenticated"]} <nil>`

not getting why :( im passing the public key and the signature in the header,
the signature.. i think im doing it right in the hmac-sha512. I'm quering the
user info url <https://www.cryptsy.com/pages/apiv2/user>, which as stated in
the api site doesn't have any extra query variables so the nonce is the only
one needed..

Have googled about restfull api's but haven't been able to find any answer :(
starting to not let me sleep at night since i think that what im doing is
kinda right.. really cant spot the error..

Anyone out there that could try and help me with this?

Thxs a lot :)


Answer: I see the issue with `result := fmt.Sprintf("%d", nonce)`. The code that
corresponds to the Python code should be something like

    
    
    result :=  fmt.Sprintf("nonce=%d", nonce)
    

Could you please check it with this fix?

I also can observe a major difference in how the request is sending. The
Python version is
([link](https://github.com/ScriptProdigy/CryptsyPythonV2/blob/master/Cryptsy.py)):

    
    
            ret = requests.get(link,
                               params=query,
                               headers=headers,
                               verify=False)
    

but your code is does not send `params` with added nonce, etc. I think it
should be something like

    
    
    rawurl = fmt.Sprintf("%s%s/%s?%s", API_BASE_CRY ,API_VERSION_CRY , ressource, queryStr)
    

where queryStr should contain nonce, etc.

