Uri BurstRequestUri(string requestType, QueryString parameters = default(QueryString)) { parameters = parameters.Add("requestType", requestType); return(_uriFactory.GetUri(requestType, "/burst", parameters)); }
public async Task <IActionResult> RespondAsync(string requestType) { Uri baseUri = null; switch (requestType) { case "submitNonce": if (!HttpMethods.IsPost(Request.Method)) { ModelState.AddModelError("", "This request must be sent as a POST request."); } if (!ulong.TryParse(GetQueryParameter("blockHeight"), out var blockHeight)) { ModelState.AddModelError("blockHeight", "Your miner must send blockHeight with the request to mine in this pool."); } if (!ulong.TryParse(GetQueryParameter("nonce"), out var nonce)) { ModelState.AddModelError("nonce", "Nonce is invalid or missing, a valid nonce is required."); } ulong? accountId = null; string secretPhrase = GetQueryParameter("secretPhrase"); if (ulong.TryParse(GetQueryParameter("accountId"), out var accountIdTemp)) { accountId = accountIdTemp; } else if (HasParameter("accountId")) { ModelState.AddModelError("accountId", "Invalid account ID"); } if (accountId == null && secretPhrase == null) { ModelState.AddModelError(nameof(accountId), "Account ID or secretPhrase is required."); ModelState.AddModelError(nameof(secretPhrase), "Account ID or secretPhrase is required."); } if (!ModelState.IsValid) { return(BadRequest(ModelState)); } var block = await _blockHeightTracker.GetCurrentBlockHeightAsync(); if (blockHeight > 0) { bool firstWait = true; while (true) { block = await _blockHeightTracker.GetCurrentBlockHeightAsync(); if (!ulong.TryParse(block.Height, out var newHeight)) { await Task.Delay(250, HttpContext.RequestAborted).ConfigureAwait(false); continue; } if (newHeight == blockHeight) { break; } else if (newHeight > blockHeight || (newHeight + _configuration.GetSection("Pool").GetValue <ulong>("MaximumPremineBlocks", 0L)) < blockHeight) { return(Ok(new { error = $"Your deadline is for a different block, your block: {blockHeight}, current block: {newHeight}" })); } else { await Task.Delay(100); } if (firstWait) { _logger.LogInformation($"Client {HttpContext.Connection.RemoteIpAddress} submitted a nonce for a future block, blocking client until block {blockHeight} appears on the network."); firstWait = false; } } } var queryString = new QueryString(); queryString = queryString .Add("requestType", "submitNonce") .Add("nonce", nonce.ToString()) .Add("blockHeight", blockHeight.ToString()); if (accountId != null) { queryString = queryString.Add("accountId", accountId.Value.ToString()); } var poolSecret = GetPoolSecretPhrase(); secretPhrase = secretPhrase ?? poolSecret; if (!string.IsNullOrWhiteSpace(secretPhrase)) { queryString = queryString.Add("secretPhrase", secretPhrase ?? poolSecret); } bool isPoolUser = string.IsNullOrWhiteSpace(secretPhrase) || (!string.IsNullOrWhiteSpace(poolSecret) && string.Equals(poolSecret, secretPhrase)); try { var response = await _httpClient.SendAsync(CreateProxyHttpRequest(HttpContext, _uriFactory.GetUri(requestType, "/burst", Request.QueryString)), HttpContext.RequestAborted).ConfigureAwait(false); var responseStringJson = await response.Content.ReadAsStringAsync().ConfigureAwait(false); var responseObject = JObject.Parse(responseStringJson); var newContent = new StringContent(responseStringJson); newContent.Headers.Clear(); foreach (var header in response.Content.Headers) { newContent.Headers.TryAddWithoutValidation(header.Key, header.Value); } response.Content = newContent; if (accountId != null && responseObject.TryGetValue("deadline", StringComparison.OrdinalIgnoreCase, out var deadlineToken)) { var deadline = deadlineToken.ToObject <ulong>(); var shares = _shareCalculator.GetShares(deadline, ulong.Parse(block.Height)); if (isPoolUser) { _logger.LogInformation($"{accountId} earned {shares} shares."); await _shareTracker.RecordSharesAsync(accountId.Value, long.Parse(block.Height), shares, nonce, deadline).ConfigureAwait(false); } else { _logger.LogInformation($"{accountId} earned {shares} shares, but they are not a member of the pool. Not recording."); } } return(ResponseMessage(response)); } catch (Exception ex) { _logger.LogError(ex, "Failed submit nonce to server, or failed to record shares."); return(BadGateway()); } default: baseUri = _uriFactory.GetUri(requestType, "/burst", Request.QueryString); var request = CreateProxyHttpRequest(Request.HttpContext, baseUri); try { var response = await _httpClient.SendAsync(request, Request.HttpContext.RequestAborted); return(ResponseMessage(response)); } catch { return(BadGateway()); } } }