public void SubmitSolution(string digest, string fromAddress, string challenge, string difficulty, string target, string solution, Miner.IMiner sender)
        {
            lock (this)
            {
                if (m_submittedChallengeList.Contains(challenge))
                {
                    Program.Print(string.Format("[INFO] Submission cancelled, nonce has been submitted for the current challenge."));
                    return;
                }

                var transactionID = string.Empty;
                var gasLimit      = new HexBigInteger(1704624ul);
                var userGas       = new HexBigInteger(UnitConversion.Convert.ToWei(new BigDecimal(m_gasToMine), UnitConversion.EthUnit.Gwei));

                var oSolution = new BigInteger(Utils.Numerics.HexStringToByte32Array(solution).ToArray());
                // Note: do not directly use -> new HexBigInteger(solution).Value
                //Because two's complement representation always interprets the highest-order bit of the last byte in the array
                //(the byte at position Array.Length- 1) as the sign bit,
                //the method returns a byte array with an extra element whose value is zero
                //to disambiguate positive values that could otherwise be interpreted as having their sign bits set.

                var dataInput = new object[] { oSolution, HexByteConvertorExtensions.HexToByteArray(digest) };

                var retryCount = 0u;
                while (string.IsNullOrWhiteSpace(transactionID))
                {
                    try
                    {
                        var txCount = m_web3.Eth.Transactions.GetTransactionCount.SendRequestAsync(fromAddress).Result;

                        var estimatedGasLimit = m_mintMethod.EstimateGasAsync(from: fromAddress,
                                                                              gas: gasLimit,
                                                                              value: new HexBigInteger(0),
                                                                              functionInput: dataInput).Result;

                        var transaction = m_mintMethod.CreateTransactionInput(from: fromAddress,
                                                                              gas: estimatedGasLimit,
                                                                              gasPrice: userGas,
                                                                              value: new HexBigInteger(0),
                                                                              functionInput: dataInput);

                        var encodedTx = Web3.OfflineTransactionSigner.SignTransaction(privateKey: m_account.PrivateKey,
                                                                                      to: m_contract.Address,
                                                                                      amount: 0,
                                                                                      nonce: txCount.Value,
                                                                                      gasPrice: userGas,
                                                                                      gasLimit: estimatedGasLimit,
                                                                                      data: transaction.Data);

                        if (!Web3.OfflineTransactionSigner.VerifyTransaction(encodedTx))
                        {
                            throw new Exception("Failed to verify transaction.");
                        }

                        transactionID = m_web3.Eth.Transactions.SendRawTransaction.SendRequestAsync("0x" + encodedTx).Result;

                        if (!string.IsNullOrWhiteSpace(transactionID))
                        {
                            if (!m_submittedChallengeList.Contains(challenge))
                            {
                                m_submittedChallengeList.Insert(0, challenge);
                                if (m_submittedChallengeList.Count > 100)
                                {
                                    m_submittedChallengeList.Remove(m_submittedChallengeList.Last());
                                }
                            }

                            Task.Factory.StartNew(() => GetTransactionReciept(transactionID, fromAddress, gasLimit, userGas));
                        }
                    }
                    catch (AggregateException ex)
                    {
                        var errorMessage = "[ERROR] " + ex.Message;

                        foreach (var iEx in ex.InnerExceptions)
                        {
                            errorMessage += "\n " + iEx.Message;
                        }

                        Program.Print(errorMessage);
                        if (m_submittedChallengeList.Contains(challenge))
                        {
                            return;
                        }
                    }
                    catch (Exception ex)
                    {
                        var errorMessage = "[ERROR] " + ex.Message;

                        if (ex.InnerException != null)
                        {
                            errorMessage += "\n " + ex.InnerException.Message;
                        }

                        Program.Print(errorMessage);
                        if (m_submittedChallengeList.Contains(challenge) || ex.Message == "Failed to verify transaction.")
                        {
                            return;
                        }
                    }

                    System.Threading.Thread.Sleep(1000);
                    if (string.IsNullOrWhiteSpace(transactionID))
                    {
                        retryCount++;
                    }

                    if (retryCount > 10)
                    {
                        Program.Print("[ERROR] Failed to submit solution for more than 10 times, please check settings.");
                        sender.StopMining();
                    }
                }
            }
        }
        private MiningParameters(string ethAddress,
                                 Function getMiningDifficulty,
                                 Function getMiningTarget,
                                 Function getChallengeNumber)
        {
            EthAddress = ethAddress;

            var retryCount = 0;
            var exceptions = new List <Exception>();

            while (retryCount < 10)
            {
                try
                {
                    MiningDifficulty = new HexBigInteger(getMiningDifficulty.CallAsync <BigInteger>().Result);
                    break;
                }
                catch (AggregateException ex)
                {
                    retryCount++;
                    if (retryCount == 10)
                    {
                        exceptions.Add(ex.InnerExceptions[0]);
                    }
                    else
                    {
                        Task.Delay(200).Wait();
                    }
                }
                catch (Exception ex)
                {
                    retryCount++;
                    if (retryCount == 10)
                    {
                        exceptions.Add(ex);
                    }
                }
            }

            while (retryCount < 10)
            {
                try
                {
                    MiningTarget             = new HexBigInteger(getMiningTarget.CallAsync <BigInteger>().Result);
                    MiningTargetByte32       = Utils.Numerics.FilterByte32Array(MiningTarget.Value.ToByteArray(littleEndian: false));
                    MiningTargetByte32String = Utils.Numerics.BigIntegerToByte32HexString(MiningTarget.Value);
                    break;
                }
                catch (AggregateException ex)
                {
                    retryCount++;
                    if (retryCount == 10)
                    {
                        exceptions.Add(ex.InnerExceptions[0]);
                    }
                    else
                    {
                        Task.Delay(200).Wait();
                    }
                }
                catch (Exception ex)
                {
                    retryCount++;
                    if (retryCount == 10)
                    {
                        exceptions.Add(ex);
                    }
                    else
                    {
                        Task.Delay(200).Wait();
                    }
                }
            }

            while (retryCount < 10)
            {
                try
                {
                    ChallengeNumberByte32       = Utils.Numerics.FilterByte32Array(getChallengeNumber.CallAsync <byte[]>().Result);
                    ChallengeNumber             = new HexBigInteger(HexByteConvertorExtensions.ToHex(ChallengeNumberByte32, prefix: true));
                    ChallengeNumberByte32String = Utils.Numerics.BigIntegerToByte32HexString(ChallengeNumber.Value);
                    break;
                }
                catch (AggregateException ex)
                {
                    retryCount++;
                    if (retryCount == 10)
                    {
                        exceptions.Add(ex.InnerExceptions[0]);
                    }
                    else
                    {
                        Task.Delay(200).Wait();
                    }
                }
                catch (Exception ex)
                {
                    retryCount++;
                    if (retryCount == 10)
                    {
                        exceptions.Add(ex);
                    }
                    else
                    {
                        Task.Delay(200).Wait();
                    }
                }
            }

            var exMessage = string.Join(Environment.NewLine, exceptions.Select(ex => ex.Message));

            if (exceptions.Any())
            {
                throw new Exception(exMessage);
            }
        }