private string CalculateMinedBlockHash(Block blockCandidate, MinedBlockPostModel minedBlock) { string blockData = blockCandidate.Index.ToString() + blockCandidate.Transactions.Count.ToString() + blockCandidate.BlockDataHash; var nonceStr = minedBlock.Nonce.ToString(); var data = blockData + minedBlock.DateCreated + nonceStr; var blockHash = this.cryptographyHelpers.CalcSHA256(data); string requiredLeadingZeroes = new String('0', blockCandidate.Difficulty); if (blockHash.StartsWith(requiredLeadingZeroes)) { return(blockHash); } throw new Exception( $"Can not calculate valid block {blockCandidate.Index} hash: hash {blockHash}, nonce {nonceStr}, difficulty {blockCandidate.Difficulty.ToString()}" ); }
public IActionResult SubmitBlock([FromBody] MinedBlockPostModel minedBlock) { if (!ModelState.IsValid) { return(BadRequest()); } if (!this.dataService.MiningJobs.ContainsKey(minedBlock.BlockDataHash)) { return(NotFound()); } this.nodeService.VerifyMinedJob(minedBlock); var response = new { Status = "accepted", Message = string.Format("Block accepted, reward paid.") }; return(Ok(response)); }
public void VerifyMinedJob(MinedBlockPostModel minedBlock) { var blockCandidate = this.dataService.MiningJobs[minedBlock.BlockDataHash]; try { var blockHash = this.CalculateMinedBlockHash(blockCandidate, minedBlock); // if next block - add to blockchain and notify peers if (blockCandidate.Index == (ulong)this.dataService.Blocks.Count + 1) { blockCandidate.Nonce = minedBlock.Nonce; blockCandidate.BlockHash = blockHash; blockCandidate.DateCreated = DateTime.Parse(minedBlock.DateCreated); this.AddBlockToBlockchain(blockCandidate); //remove pending transactions that are already included in the blockchain blockCandidate.Transactions.ForEach(transaction => { this.dataService.PendingTransactions.Remove( this.dataService .PendingTransactions .Single(t => t.TransactionHash == transaction.TransactionHash) ); }); this.logger.LogInformation($"Block {blockCandidate.Index} with hash {blockHash} added to blockchain"); } else { this.logger.LogInformation($"Block candidate {blockCandidate.Index} with hash {blockHash} already exists in blockckain"); } } catch (Exception e) { this.logger.LogInformation(e.Message); } }
public static async Task MineAsync(IHttpHelpers httpHelpers, IDateTimeHelpers dateTimeHelpers, Logger log) { HttpStatusCode statusCode = HttpStatusCode.RequestTimeout; Stopwatch sw = new Stopwatch(); TimeSpan maxTaskLength = new TimeSpan(0, 0, 2); // 2 seconds while (true) { sw.Start(); MiningJob miningJob = null; do { try { string path = "mining/get-mining-job/{minerAddress}"; var parameter = new Parameter() { Name = "minerAddress", Value = minerAddress, Type = ParameterType.UrlSegment }; log.Information($"Trying to get mining job from node: {nodeUrl}"); Response <MiningJob> response = await httpHelpers.DoApiGet <MiningJob>(nodeUrl, path, parameter); miningJob = response.Data; statusCode = response.StatusCode; } catch (WebException e) { Console.WriteLine("WebException raised!"); Console.WriteLine("{0}\n", e.Message); } catch (Exception e) { Console.WriteLine("Exception raised!"); Console.WriteLine("Source : {0}", e.Source); Console.WriteLine("Message : {0}\n", e.Message); } } while (statusCode != HttpStatusCode.OK); log.Information($"Successfully received mining job (Block Data Hash: {miningJob.BlockDataHash}) from node!"); Console.WriteLine("Start New Mining Job:"); Console.WriteLine("Block Index: {0}", miningJob.BlockIndex); Console.WriteLine("Transactions Included: {0}", miningJob.TransactionsIncluded); Console.WriteLine("Expected Reward: {0}", miningJob.ExpectedReward); Console.WriteLine("Reward Address: {0}", miningJob.RewardAddress); Console.WriteLine("Block Data Hash: {0}", miningJob.BlockDataHash); Console.WriteLine("Difficulty: {0}", miningJob.Difficulty); bool blockFound = false; ulong nonce = 0; string timestamp = dateTimeHelpers.ConvertDateTimeToUniversalTimeISO8601String(DateTime.Now); string requiredLeadingZeroes = new String('0', miningJob.Difficulty); string blockData = miningJob.BlockIndex.ToString() + miningJob.TransactionsIncluded.ToString() + miningJob.BlockDataHash; string data; string blockHash; while (!blockFound && nonce < uint.MaxValue) { data = blockData + timestamp + nonce.ToString(); blockHash = ByteArrayToHexString(Sha256(Encoding.UTF8.GetBytes(data))); if (blockHash.StartsWith(requiredLeadingZeroes)) { Console.WriteLine("Block Mined!"); Console.WriteLine($"Block Hash: {blockHash}\n"); var minedBlock = new MinedBlockPostModel() { DateCreated = timestamp, Nonce = nonce, BlockDataHash = miningJob.BlockDataHash }; int retries = 0; do { try { statusCode = HttpStatusCode.RequestTimeout; string path = "mining/submit-mined-block"; HttpResponseMessage response = await httpHelpers.DoApiPost(nodeUrl, path, minedBlock); statusCode = response.StatusCode; string statusDescription = response.ReasonPhrase; log.Information($"Sent request to: {nodeUrl} with mined block (hash: {blockHash})!"); Console.WriteLine(statusDescription); log.Information($"Received response from {nodeUrl} - status code: {statusCode}, description: {statusDescription}"); } catch (WebException e) { Console.WriteLine("WebException raised!"); Console.WriteLine("{0}\n", e.Message); } catch (Exception e) { Console.WriteLine("Exception raised!"); Console.WriteLine("Source : {0}", e.Source); Console.WriteLine("Message : {0}\n", e.Message); } System.Threading.Thread.Sleep(1000); } while (statusCode != HttpStatusCode.OK && retries++ < 3); blockFound = true; break; } // print intermediate data if (nonce % 1000000 == 0) { Console.WriteLine(timestamp); Console.WriteLine($"Nonce: {nonce}"); Console.WriteLine($"Block Hash: {blockHash}\n"); } // get new timestamp on every 100000 iterations if (nonce % 100000 == 0) { timestamp = dateTimeHelpers.ConvertDateTimeToUniversalTimeISO8601String(DateTime.Now); } nonce++; if (maxTaskLength < sw.Elapsed) { sw.Reset(); break; } } } }