public BlockValidationResult TryAddBlock(MinedBlock minedBlock, out Block candidateBlock)
        {
            bool found = _miningJobs.TryGetValue(minedBlock.BlockDataHash, out candidateBlock);

            if (!found)
            {
                return(BlockValidationResult.BlockNotFound);
            }

            string difficultyCheck = new string('0', _nodeSettings.CurrentDifficulty);

            if (!minedBlock.BlockHash.StartsWith(difficultyCheck))
            {
                return(BlockValidationResult.BlockHashDifficultyMismatch);
            }

            string blockHashCheck = HashUtils.ComputeBlockSha256Hash(
                minedBlock.BlockDataHash,
                minedBlock.DateCreated,
                minedBlock.Nonce);

            if (blockHashCheck != minedBlock.BlockHash)
            {
                return(BlockValidationResult.InvalidBlockHash);
            }

            if (candidateBlock.Index != _chain.Count)
            {
                return(BlockValidationResult.BlockAlreadyMined);
            }

            // block found, will be added in chain
            _chain.Add(candidateBlock);
            _miningJobs.Clear();

            UpdateCandidateBlockData(candidateBlock, minedBlock);
            MoveBlockTransactionsToConfirmed(candidateBlock);

            _peerSynchronizationService.BroadcastNewBlockNotification(
                _chain.Count,
                GetCumulativeDifficulty(),
                _nodeSettings.NodeUrl);

            return(BlockValidationResult.Ok);
        }
        private static async Task MainAsync(string[] args)
        {
            string nodeBaseUrl;
            string minerAddress;

            if (args.Length < 2)
            {
                nodeBaseUrl  = "http://localhost:64149";
                minerAddress = "9a9f082f37270ff54c5ca4204a0e4da6951fe917";
            }
            else
            {
                nodeBaseUrl  = args[0];
                minerAddress = args[1];
            }

            TimeSpan    maxJobDuration = new TimeSpan(0, 0, 5);
            INodeClient nodeClient     = new NodeClient(nodeBaseUrl);
            Stopwatch   stopwatch      = new Stopwatch();

            do
            {
                stopwatch.Start();

                MiningJob job = await GetMiningJob(nodeClient, minerAddress).ConfigureAwait(false);

                string   difficultyCheck = new string('0', job.Difficulty);
                DateTime dateCreated     = DateTime.UtcNow;
                ulong    nonce           = 0;

                while (nonce < ulong.MaxValue)
                {
                    string guess = HashUtils.ComputeBlockSha256Hash(job.BlockDataHash, dateCreated, nonce);
                    if (guess.StartsWith(difficultyCheck))
                    {
                        // block found, send it to the node
                        bool submitted = await SubmitMinedBlock(
                            nodeClient,
                            minerAddress,
                            5,
                            job.BlockDataHash,
                            dateCreated,
                            nonce,
                            guess).ConfigureAwait(false);

                        Console.WriteLine($"Submitting mining result {(submitted ? "successful" : "failed")}.");
                        break;
                    }

                    if (maxJobDuration < stopwatch.Elapsed)
                    {
                        stopwatch.Reset();
                        break;
                    }

                    // get new timestamp on every 100,000 iterations
                    if (nonce % 100000 == 0)
                    {
                        dateCreated = DateTime.UtcNow;
                    }

                    nonce++;
                }
            } while (true);
        }