Beispiel #1
0
        public async Task <object> GenerateNonceAsync()
        {
            await new SynchronizationContextRemover();

            if (NonceOffset.Ticks == 0)
            {
                await OnGetNonceOffset();
            }

            object nonce;

            while (true)
            {
                lock (this)
                {
                    // some API (Binance) have a problem with requests being after server time, subtract of offset can help
                    DateTime now = CryptoUtility.UtcNow - NonceOffset;
                    switch (NonceStyle)
                    {
                    case NonceStyle.Ticks:
                        nonce = now.Ticks;
                        break;

                    case NonceStyle.TicksString:
                    case NonceStyle.Iso8601:
                        nonce = now.Ticks.ToStringInvariant();
                        break;

                    case NonceStyle.TicksThenIncrement:
                        if (lastNonce == 0m)
                        {
                            nonce = now.Ticks;
                        }
                        else
                        {
                            nonce = (long)(lastNonce + 1m);
                        }
                        break;

                    case NonceStyle.UnixMilliseconds:
                        nonce = (long)now.UnixTimestampFromDateTimeMilliseconds();
                        break;

                    case NonceStyle.UnixMillisecondsString:
                        nonce = ((long)now.UnixTimestampFromDateTimeMilliseconds()).ToStringInvariant();
                        break;

                    case NonceStyle.UnixMillisecondsThenIncrement:
                        if (lastNonce == 0m)
                        {
                            nonce = (long)now.UnixTimestampFromDateTimeMilliseconds();
                        }
                        else
                        {
                            nonce = (long)(lastNonce + 1m);
                        }
                        break;

                    case NonceStyle.UnixSeconds:
                        nonce = now.UnixTimestampFromDateTimeSeconds();
                        break;

                    case NonceStyle.UnixSecondsString:
                        nonce = now.UnixTimestampFromDateTimeSeconds().ToStringInvariant();
                        break;

                    case NonceStyle.Int32File:
                    case NonceStyle.Int64File:
                    {
                        // why an API would use a persistent incrementing counter for nonce is beyond me, ticks is so much better with a sliding window...
                        // making it required to increment by 1 is also a pain - especially when restarting a process or rebooting.
                        string tempFile = Path.Combine(Path.GetTempPath(), (PublicApiKey?.ToUnsecureString() ?? "unknown_pub_key") + ".nonce");
                        if (!File.Exists(tempFile))
                        {
                            File.WriteAllText(tempFile, "0");
                        }
                        unchecked
                        {
                            long longNonce = File.ReadAllText(tempFile).ConvertInvariant <long>() + 1;
                            long maxValue  = (NonceStyle == NonceStyle.Int32File ? int.MaxValue : long.MaxValue);
                            if (longNonce < 1 || longNonce > maxValue)
                            {
                                throw new APIException($"Nonce {longNonce.ToStringInvariant()} is out of bounds, valid ranges are 1 to {maxValue.ToStringInvariant()}, " +
                                                       $"please regenerate new API keys. Please contact {Name} API support and ask them to change to a sensible nonce algorithm.");
                            }
                            File.WriteAllText(tempFile, longNonce.ToStringInvariant());
                            nonce = longNonce;
                        }
                        break;
                    }

                    case NonceStyle.ExpiresUnixMilliseconds:
                        nonce = (long)now.UnixTimestampFromDateTimeMilliseconds();
                        break;

                    case NonceStyle.ExpiresUnixSeconds:
                        nonce = (long)now.UnixTimestampFromDateTimeSeconds();
                        break;

                    default:
                        throw new InvalidOperationException("Invalid nonce style: " + NonceStyle);
                    }

                    // check for duplicate nonce
                    decimal convertedNonce = nonce.ConvertInvariant <decimal>();
                    if (lastNonce != convertedNonce || NonceStyle == NonceStyle.ExpiresUnixSeconds || NonceStyle == NonceStyle.ExpiresUnixMilliseconds)
                    {
                        lastNonce = convertedNonce;
                        break;
                    }
                }

                // wait 1 millisecond for a new nonce
                await Task.Delay(1);
            }

            return(nonce);
        }
 protected override async Task ProcessRequestAsync(HttpWebRequest request, Dictionary <string, object> payload)
 {
     // only authenticated requests write json, everything uses GET and url params
     if (CanMakeAuthenticatedRequest(payload))
     {
         request.Headers["Authorization"] = CryptoUtility.BasicAuthenticationString(PublicApiKey.ToUnsecureString(), PrivateApiKey.ToUnsecureString());
         if (request.Method == "POST")
         {
             await CryptoUtility.WritePayloadJsonToRequestAsync(request, payload);
         }
     }
 }
Beispiel #3
0
        protected override async Task <IWebSocket> OnUserDataWebSocketAsync(Action <object> callback)
        {
            return(await ConnectPublicWebSocketAsync("/", async (_socket, msg) =>
            {
                var token = msg.ToStringFromUTF8();
                var response = JsonConvert.DeserializeObject <BaseMessage>(token);
                switch (response.Type)
                {
                case ResponseType.Subscriptions:
                    var subscription = JsonConvert.DeserializeObject <Subscription>(token);
                    if (subscription.Channels == null || !subscription.Channels.Any())
                    {
                        Trace.WriteLine($"{nameof(OnUserDataWebSocketAsync)}() no channels subscribed");
                    }
                    else
                    {
                        Trace.WriteLine($"{nameof(OnUserDataWebSocketAsync)}() subscribed to " +
                                        $"{string.Join(",", subscription.Channels.Select(c => c.ToString()))}");
                    }
                    break;

                case ResponseType.Ticker:
                    throw new NotImplementedException($"Not expecting type {response.Type} in {nameof(OnUserDataWebSocketAsync)}()");

                case ResponseType.Snapshot:
                    throw new NotImplementedException($"Not expecting type {response.Type} in {nameof(OnUserDataWebSocketAsync)}()");

                case ResponseType.L2Update:
                    throw new NotImplementedException($"Not expecting type {response.Type} in {nameof(OnUserDataWebSocketAsync)}()");

                case ResponseType.Heartbeat:
                    var heartbeat = JsonConvert.DeserializeObject <Heartbeat>(token);
                    Trace.WriteLine($"{nameof(OnUserDataWebSocketAsync)}() heartbeat received {heartbeat}");
                    break;

                case ResponseType.Received:
                    var received = JsonConvert.DeserializeObject <Received>(token);
                    callback(received.ExchangeOrderResult);
                    break;

                case ResponseType.Open:
                    var open = JsonConvert.DeserializeObject <Open>(token);
                    callback(open.ExchangeOrderResult);
                    break;

                case ResponseType.Done:
                    var done = JsonConvert.DeserializeObject <Done>(token);
                    callback(done.ExchangeOrderResult);
                    break;

                case ResponseType.Match:
                    var match = JsonConvert.DeserializeObject <Match>(token);
                    callback(match.ExchangeOrderResult);
                    break;

                case ResponseType.LastMatch:
                    //var lastMatch = JsonConvert.DeserializeObject<LastMatch>(token);
                    throw new NotImplementedException($"Not expecting type {response.Type} in {nameof(OnUserDataWebSocketAsync)}()");

                case ResponseType.Error:
                    var error = JsonConvert.DeserializeObject <Error>(token);
                    throw new APIException($"{error.Reason}: {error.Message}");

                case ResponseType.Change:
                    var change = JsonConvert.DeserializeObject <Change>(token);
                    callback(change.ExchangeOrderResult);
                    break;

                case ResponseType.Activate:
                    var activate = JsonConvert.DeserializeObject <Activate>(token);
                    callback(activate.ExchangeOrderResult);
                    break;

                case ResponseType.Status:
                    //var status = JsonConvert.DeserializeObject<Status>(token);
                    throw new NotImplementedException($"Not expecting type {response.Type} in {nameof(OnUserDataWebSocketAsync)}()");

                default:
                    throw new NotImplementedException($"Not expecting type {response.Type} in {nameof(OnUserDataWebSocketAsync)}()");
                }
            }, async (_socket) =>
            {
                var marketSymbols = (await GetMarketSymbolsAsync()).ToArray();
                var nonce = await GetNoncePayloadAsync();
                string timestamp = nonce["nonce"].ToStringInvariant();
                byte[] secret = CryptoUtility.ToBytesBase64Decode(PrivateApiKey);
                string toHash = timestamp + "GET" + "/users/self/verify";
                var subscribeRequest = new
                {
                    type = "subscribe",
                    channels = new object[]
                    {
                        new
                        {
                            name = "user",
                            product_ids = marketSymbols,
                        }
                    },
                    signature = CryptoUtility.SHA256SignBase64(toHash, secret),                     // signature base 64 string
                    key = PublicApiKey.ToUnsecureString(),
                    passphrase = CryptoUtility.ToUnsecureString(Passphrase),
                    timestamp = timestamp
                };
                await _socket.SendMessageAsync(subscribeRequest);
            }));
        }
Beispiel #4
0
        protected override async Task ProcessRequestAsync(HttpWebRequest request, Dictionary <string, object> payload)
        {
            // Only Private APIs are POST and need Authorization
            if (CanMakeAuthenticatedRequest(payload) && request.Method == "POST")
            {
                string requestContentBase64String = string.Empty;
                string nonce = payload["nonce"] as string;
                payload.Remove("nonce");

                string jsonContent = CryptoUtility.GetJsonForPayload(payload);
                if (!String.IsNullOrEmpty(jsonContent))
                {
                    using (MD5 md5 = MD5.Create())
                    {
                        requestContentBase64String = Convert.ToBase64String(md5.ComputeHash(Encoding.UTF8.GetBytes(jsonContent)));
                    }
                }
                else
                {
                    request.ContentLength = 0;
                }

                string baseSig   = string.Concat(PublicApiKey.ToUnsecureString(), request.Method, Uri.EscapeDataString(request.RequestUri.AbsoluteUri).ToLower(), nonce, requestContentBase64String);
                string signature = CryptoUtility.SHA256SignBase64(baseSig, Convert.FromBase64String(PrivateApiKey.ToUnsecureString()));
                request.Headers.Add(HttpRequestHeader.Authorization, string.Format("amx {0}:{1}:{2}", PublicApiKey.ToUnsecureString(), signature, nonce));

                // Cryptopia is very picky on how the payload is passed. There might be a better way to do this, but this works...
                using (Stream stream = await request.GetRequestStreamAsync())
                {
                    byte[] content = Encoding.UTF8.GetBytes(jsonContent);
                    stream.Write(content, 0, content.Length);
                }
            }
        }
Beispiel #5
0
        /// <summary>
        /// ASYNC - Generate a nonce
        /// </summary>
        /// <returns>Nonce</returns>
        public async Task <object> GenerateNonceAsync()
        {
            await new SynchronizationContextRemover();

            if (NonceOffset.Ticks == 0)
            {
                await OnGetNonceOffset();
            }

            // exclusive lock, no two nonces must match
            lock (this)
            {
                object nonce;

                while (true)
                {
                    // some API (Binance) have a problem with requests being after server time, subtract of one second fixes it
                    DateTime now = DateTime.UtcNow - NonceOffset;
                    Task.Delay(1).Wait();

                    switch (NonceStyle)
                    {
                    case NonceStyle.Ticks:
                        nonce = now.Ticks;
                        break;

                    case NonceStyle.TicksString:
                        nonce = now.Ticks.ToStringInvariant();
                        break;

                    case NonceStyle.UnixMilliseconds:
                        nonce = (long)now.UnixTimestampFromDateTimeMilliseconds();
                        break;

                    case NonceStyle.UnixMillisecondsString:
                        nonce = ((long)now.UnixTimestampFromDateTimeMilliseconds()).ToStringInvariant();
                        break;

                    case NonceStyle.UnixSeconds:
                        nonce = now.UnixTimestampFromDateTimeSeconds();
                        break;

                    case NonceStyle.UnixSecondsString:
                        nonce = now.UnixTimestampFromDateTimeSeconds().ToStringInvariant();
                        break;

                    case NonceStyle.IntegerFile:
                    {
                        // why an API would use a persistent incrementing counter for nonce is beyond me, ticks is so much better with a sliding window...
                        string tempFile = Path.Combine(Path.GetTempPath(), PublicApiKey.ToUnsecureString() + ".nonce");
                        if (!File.Exists(tempFile))
                        {
                            File.WriteAllText(tempFile, "0");
                        }
                        unchecked
                        {
                            int intNonce = int.Parse(File.ReadAllText(tempFile), CultureInfo.InvariantCulture) + 1;
                            if (intNonce < 1)
                            {
                                throw new APIException("Nonce is out of bounds of a signed 32 bit integer (1 - " + int.MaxValue.ToStringInvariant() +
                                                       "), please regenerate new API keys. Please contact the API support and ask them to change this horrible nonce behavior.");
                            }
                            nonce = (long)intNonce;
                            File.WriteAllText(tempFile, intNonce.ToStringInvariant());
                        }
                    } break;

                    default:
                        throw new InvalidOperationException("Invalid nonce style: " + NonceStyle);
                    }

                    // check for duplicate nonce
                    decimal convertedNonce = nonce.ConvertInvariant <decimal>();
                    if (lastNonce != convertedNonce)
                    {
                        lastNonce = convertedNonce;
                        break;
                    }
                }

                return(nonce);
            }
        }
 protected override Uri ProcessRequestUrl(UriBuilder url, Dictionary <string, object> payload)
 {
     if (CanMakeAuthenticatedRequest(payload))
     {
         // payload is ignored, except for the nonce which is added to the url query - HitBTC puts all the "post" parameters in the url query instead of the request body
         var    query    = HttpUtility.ParseQueryString(url.Query);
         string newQuery = "nonce=" + payload["nonce"].ToString() + "&apikey=" + PublicApiKey.ToUnsecureString() + (query.Count == 0 ? string.Empty : "&" + query.ToString()) +
                           (payload.Count > 1 ? "&" + GetFormForPayload(payload, false) : string.Empty);
         url.Query = newQuery;
         return(url.Uri);
     }
     return(base.ProcessRequestUrl(url, payload));
 }