private async Task <string> SignIfRequiredAsync <T>(T response) { string payload = HelperTools.JSONSerialize(response, false); if (minerId == null) { // Do not sign if we do not have miner id return(payload); } lastMinerId ??= await minerId.GetCurrentMinerIdAsync(); async Task <JsonEnvelope> TryToSign() { async Task <(string signature, string publicKey)> signWithMinerId(string sigHashHex) { var signature = await minerId.SignWithMinerIdAsync(lastMinerId, sigHashHex); return(signature, lastMinerId); } var envelope = await JsonEnvelopeSignature.CreateJSonSignatureAsync(payload, signWithMinerId); // Verify a signature - some implementation might have incorrect race conditions when rotating the keys if (!JsonEnvelopeSignature.VerifySignature(envelope)) { return(null); } return(envelope); } var jsonEnvelope = await TryToSign(); if (jsonEnvelope == null) { throw new Exception("Error while validating signature. Possible reason: incorrect configuration or key rotation"); } return(HelperTools.JSONSerialize(new SignedPayloadViewModel(jsonEnvelope), true)); }
/// <summary> /// Signs response if required. If response already contains minerId (for example as part of policy quote), pass it in as currentMinerId /// To make sure that correct key is used even key rotation just occured /// </summary> /// <typeparam name="T"></typeparam> /// <param name="response"></param> /// <param name="responseMinerId"></param> /// <returns></returns> async Task <ActionResult> SignIfRequiredAsync <T>(T response, string responseMinerId) { string payload = HelperTools.JSONSerialize(response, false); if (string.IsNullOrEmpty(responseMinerId)) { // Do not sing if we do not have miner id return(Ok(new JSONEnvelopeViewModel(payload))); } async Task <(string signature, string publicKey)> SignWithMinerId(string sigHashHex) { var signature = await minerId.SignWithMinerIdAsync(responseMinerId, sigHashHex); return(signature, responseMinerId); } var jsonEnvelope = await JsonEnvelopeSignature.CreateJSonSignatureAsync(payload, SignWithMinerId); var ret = new SignedPayloadViewModel(jsonEnvelope); return(Ok(ret)); }
static async Task SendTransactionsBatch(IEnumerable <string> transactions, HttpClient client, Stats stats, string url, string callbackUrl, string callbackToken, string callbackEncryption) { var query = new List <string>(); string doCallbacks = string.IsNullOrEmpty(callbackUrl) ? "false" : "true"; query.Add($"defaultDsCheck={doCallbacks}"); query.Add($"defaultMerkleProof={doCallbacks}"); if (!string.IsNullOrEmpty(callbackUrl)) { query.Add("defaultCallbackUrl=" + WebUtility.UrlEncode(callbackUrl)); if (!string.IsNullOrEmpty(callbackToken)) { query.Add("defaultCallbackToken=" + WebUtility.UrlEncode(callbackToken)); } if (!string.IsNullOrEmpty(callbackEncryption)) { query.Add("defaultCallbackEncryption=" + WebUtility.UrlEncode(callbackEncryption)); } } string queryString = string.Join("&", query.ToArray()); var ub = new UriBuilder(url); if (ub.Query.Length == 0) { ub.Query = queryString; // automatically adds ? at the beginning } else { ub.Query = ub.Query.Substring(1) + "&" + queryString; // remove leading ? it is added back automatically } string urlWithParams = ub.Uri.ToString(); string callbackHost = ""; if (!string.IsNullOrEmpty(callbackUrl)) { callbackHost = new Uri(callbackUrl).Host; } // We currently submit through REST interface., We could also use binary interface var request = transactions.Select(t => new SubmitTransactionViewModel { RawTx = t, // All other parameters are passed in query string CallbackUrl = null, CallbackToken = null, CallbackEncryption = null, MerkleProof = null, DsCheck = null }).ToArray(); var requestString = HelperTools.JSONSerialize(request, false); var response = await client.PostAsync(urlWithParams, new StringContent(requestString, new UTF8Encoding(false), MediaTypeNames.Application.Json)); var responseAsString = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { Console.WriteLine($"Error while submitting transaction request {responseAsString}"); stats.IncrementRequestErrors(); } else { var rEnvelope = HelperTools.JSONDeserialize <SignedPayloadViewModel>(responseAsString); var r = HelperTools.JSONDeserialize <SubmitTransactionsResponseViewModel>(rEnvelope.Payload); int printLimit = 10; var errorItems = r.Txs.Where(t => t.ReturnResult != "success").ToArray(); var okItems = r.Txs.Where(t => t.ReturnResult == "success").ToArray(); stats.AddRequestTxFailures(callbackHost, errorItems.Select(x => new uint256(x.Txid))); stats.AddOkSubmited(callbackHost, okItems.Select(x => new uint256(x.Txid))); var errors = errorItems .Select(t => t.Txid + " " + t.ReturnResult + " " + t.ResultDescription).ToArray(); var limitedErrors = string.Join(Environment.NewLine, errors.Take(printLimit)); if (errors.Any()) { Console.WriteLine($"Error while submitting transactions. Printing up to {printLimit} out of {errors.Length} errors : {limitedErrors}"); } } }