protected override async Task PostStartInitAsync()
        {
            var commands = new[]
            {
                new DaemonCmd(EC.GetNetVersion),
                new DaemonCmd(EC.GetAccounts),
                new DaemonCmd(EC.GetCoinbase),
                new DaemonCmd(EC.ParityVersion),
                new DaemonCmd(EC.ParityChain),
            };

            var results = await daemon.ExecuteBatchAnyAsync(commands);

            if (results.Any(x => x.Error != null))
            {
                if (results[4].Error != null)
                {
                    logger.ThrowLogPoolStartupException($"Looks like you are NOT running 'Parity' as daemon which is not supported - https://parity.io/", LogCat);
                }

                var errors = results.Where(x => x.Error != null)
                             .ToArray();

                if (errors.Any())
                {
                    logger.ThrowLogPoolStartupException($"Init RPC failed: {string.Join(", ", errors.Select(y => y.Error.Message))}", LogCat);
                }
            }

            // extract results
            var netVersion    = results[0].Response.ToObject <string>();
            var accounts      = results[1].Response.ToObject <string[]>();
            var coinbase      = results[2].Response.ToObject <string>();
            var parityVersion = results[3].Response.ToObject <JObject>();
            var parityChain   = results[4].Response.ToObject <string>();

            // ensure pool owns wallet
            if (!accounts.Contains(poolConfig.Address) || coinbase != poolConfig.Address)
            {
                logger.ThrowLogPoolStartupException($"Daemon does not own pool-address '{poolConfig.Address}'", LogCat);
            }

            EthereumUtils.DetectNetworkAndChain(netVersion, parityChain, out networkType, out chainType);

            ConfigureRewards();

            // update stats
            BlockchainStats.RewardType  = "POW";
            BlockchainStats.NetworkType = $"{chainType}";

            await UpdateNetworkStatsAsync();

            // make sure we have a current DAG
            while (true)
            {
                var blockTemplate = await GetBlockTemplateAsync();

                if (blockTemplate == null)
                {
                    logger.Info(() => $"[{LogCat}] Waiting for first valid block template");

                    await Task.Delay(TimeSpan.FromSeconds(5));

                    continue;
                }

                await ethash.GetDagAsync(blockTemplate.Height);

                break;
            }

            SetupJobUpdates();
        }
Beispiel #2
0
        protected override async Task PostStartInitAsync(CancellationToken ct)
        {
            var commands = new[]
            {
                new DaemonCmd(EC.GetNetVersion),
                new DaemonCmd(EC.GetAccounts),
                new DaemonCmd(EC.GetCoinbase),
                new DaemonCmd(EC.ParityVersion),
                new DaemonCmd(EC.ParityChain),
            };

            var results = await daemon.ExecuteBatchAnyAsync(commands);

            if (results.Any(x => x.Error != null))
            {
                if (results[4].Error != null)
                {
                    logger.ThrowLogPoolStartupException($"Looks like you are NOT running 'Parity' as daemon which is not supported - https://parity.io/", LogCat);
                }

                var errors = results.Where(x => x.Error != null)
                             .ToArray();

                if (errors.Any())
                {
                    logger.ThrowLogPoolStartupException($"Init RPC failed: {string.Join(", ", errors.Select(y => y.Error.Message))}", LogCat);
                }
            }

            // extract results
            var netVersion    = results[0].Response.ToObject <string>();
            var accounts      = results[1].Response.ToObject <string[]>();
            var coinbase      = results[2].Response.ToObject <string>();
            var parityVersion = results[3].Response.ToObject <JObject>();
            var parityChain   = results[4].Response.ToObject <string>();

            // ensure pool owns wallet
            //if (clusterConfig.PaymentProcessing?.Enabled == true && !accounts.Contains(poolConfig.Address) || coinbase != poolConfig.Address)
            //    logger.ThrowLogPoolStartupException($"Daemon does not own pool-address '{poolConfig.Address}'", LogCat);

            EthereumUtils.DetectNetworkAndChain(netVersion, parityChain, out networkType, out chainType);

            if (clusterConfig.PaymentProcessing?.Enabled == true && poolConfig.PaymentProcessing?.Enabled == true)
            {
                ConfigureRewards();
            }

            // update stats
            BlockchainStats.RewardType  = "POW";
            BlockchainStats.NetworkType = $"{chainType}-{networkType}";

            await UpdateNetworkStatsAsync();

            // Periodically update network stats
            Observable.Interval(TimeSpan.FromMinutes(10))
            .Select(via => Observable.FromAsync(UpdateNetworkStatsAsync))
            .Concat()
            .Subscribe();

            if (poolConfig.EnableInternalStratum == true)
            {
                // make sure we have a current DAG
                while (true)
                {
                    var blockTemplate = await GetBlockTemplateAsync();

                    if (blockTemplate != null)
                    {
                        logger.Info(() => $"[{LogCat}] Loading current DAG ...");

                        await ethash.GetDagAsync(blockTemplate.Height, logger);

                        logger.Info(() => $"[{LogCat}] Loaded current DAG");
                        break;
                    }

                    logger.Info(() => $"[{LogCat}] Waiting for first valid block template");
                    await Task.Delay(TimeSpan.FromSeconds(5), ct);
                }

                SetupJobUpdates();
            }
        }
Beispiel #3
0
        public async Task <EthereumShare> ProcessShareAsync(StratumClient worker, string nonce, EthashFull ethash)
        {
            // duplicate nonce?
            lock (workerNonces)
            {
                RegisterNonce(worker, nonce);
            }

            // assemble full-nonce
            var context      = worker.GetContextAs <EthereumWorkerContext>();
            var fullNonceHex = context.ExtraNonce1 + nonce;
            var fullNonce    = ulong.Parse(fullNonceHex, NumberStyles.HexNumber);

            // get dag for block
            var dag = await ethash.GetDagAsync(BlockTemplate.Height);

            // compute
            if (!dag.Compute(BlockTemplate.Header.HexToByteArray(), fullNonce, out var mixDigest, out var resultBytes))
            {
                throw new StratumException(StratumError.MinusOne, "bad hash");
            }

            resultBytes.ReverseArray();

            // test if share meets at least workers current difficulty
            var resultValue       = new uint256(resultBytes);
            var resultValueBig    = resultBytes.ToBigInteger();
            var shareDiff         = (double)BigInteger.Divide(EthereumConstants.BigMaxValue, resultValueBig) / EthereumConstants.Pow2x32;
            var stratumDifficulty = context.Difficulty;
            var ratio             = shareDiff / stratumDifficulty;
            var isBlockCandidate  = resultValue <= blockTarget;

            if (!isBlockCandidate && ratio < 0.99)
            {
                // check if share matched the previous difficulty from before a vardiff retarget
                if (context.VarDiff?.LastUpdate != null && context.PreviousDifficulty.HasValue)
                {
                    ratio = shareDiff / context.PreviousDifficulty.Value;

                    if (ratio < 0.99)
                    {
                        throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})");
                    }

                    // use previous difficulty
                    stratumDifficulty = context.PreviousDifficulty.Value;
                }

                else
                {
                    throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})");
                }
            }

            // create share
            var share = new EthereumShare
            {
                BlockHeight      = (long)BlockTemplate.Height,
                IpAddress        = worker.RemoteEndpoint?.Address?.ToString(),
                Miner            = context.MinerName,
                Worker           = context.WorkerName,
                UserAgent        = context.UserAgent,
                FullNonceHex     = "0x" + fullNonceHex,
                HeaderHash       = BlockTemplate.Header,
                MixHash          = mixDigest.ToHexString(true),
                IsBlockCandidate = isBlockCandidate,
                Difficulty       = stratumDifficulty * EthereumConstants.Pow2x32,
                BlockHash        = mixDigest.ToHexString(true) // OW: is this correct?
            };

            if (share.IsBlockCandidate)
            {
                share.TransactionConfirmationData = $"{mixDigest.ToHexString(true)}:{share.FullNonceHex}";
            }

            return(share);
        }
Beispiel #4
0
        public async ValueTask <(Share Share, string FullNonceHex, string HeaderHash, string MixHash)> ProcessShareAsync(
            StratumClient worker, string nonce, EthashFull ethash, CancellationToken ct)
        {
            // duplicate nonce?
            lock (workerNonces)
            {
                RegisterNonce(worker, nonce);
            }

            // assemble full-nonce
            var context = worker.ContextAs <EthereumWorkerContext>();

            var fullNonceHex = nonce.StartsWith("0x") ? nonce.Substring(2) : nonce;

            if (context.IsNiceHashClient && !string.IsNullOrEmpty(context.ExtraNonce1))
            {
                fullNonceHex = context.ExtraNonce1 + fullNonceHex;
            }

            if (!ulong.TryParse(fullNonceHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var fullNonce))
            {
                throw new StratumException(StratumError.MinusOne, "bad nonce " + fullNonceHex);
            }

            // get dag for block
            var dag = await ethash.GetDagAsync(BlockTemplate.Height, logger, ct);

            // compute
            if (!dag.Compute(logger, BlockTemplate.Header.HexToByteArray(), fullNonce, out var mixDigest, out var resultBytes))
            {
                throw new StratumException(StratumError.MinusOne, "bad hash");
            }

            // test if share meets at least workers current difficulty
            resultBytes.ReverseInPlace();
            var resultValue       = new uint256(resultBytes);
            var resultValueBig    = resultBytes.AsSpan().ToBigInteger();
            var shareDiff         = (double)BigInteger.Divide(EthereumConstants.BigMaxValue, resultValueBig) / EthereumConstants.Pow2x32;
            var stratumDifficulty = context.Difficulty;
            var ratio             = shareDiff / stratumDifficulty;
            var isBlockCandidate  = resultValue <= blockTarget;

            if (!isBlockCandidate && ratio < 0.99)
            {
                // check if share matched the previous difficulty from before a vardiff retarget
                if (context.VarDiff?.LastUpdate != null && context.PreviousDifficulty.HasValue)
                {
                    ratio = shareDiff / context.PreviousDifficulty.Value;

                    if (ratio < 0.99)
                    {
                        throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})");
                    }

                    // use previous difficulty
                    stratumDifficulty = context.PreviousDifficulty.Value;
                }

                else
                {
                    throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})");
                }
            }

            // create share
            var share = new Share
            {
                BlockHeight      = (long)BlockTemplate.Height,
                IpAddress        = worker.RemoteEndpoint?.Address?.ToString(),
                Miner            = context.Miner,
                Worker           = context.Worker,
                UserAgent        = context.UserAgent,
                IsBlockCandidate = isBlockCandidate,
                Difficulty       = stratumDifficulty * EthereumConstants.Pow2x32,
                BlockHash        = mixDigest.ToHexString(true)
            };

            if (share.IsBlockCandidate)
            {
                fullNonceHex = "0x" + fullNonceHex;
                var headerHash = BlockTemplate.Header;
                var mixHash    = mixDigest.ToHexString(true);

                share.TransactionConfirmationData = $"{mixDigest.ToHexString(true)}:{fullNonceHex}";

                return(share, fullNonceHex, headerHash, mixHash);
            }

            return(share, null, null, null);
        }
Beispiel #5
0
        public async Task <(Share Share, string FullNonceHex, string HeaderHash, string MixHash)> ProcessShareAsync(StratumClient worker, string nonce, EthashFull ethash)
        {
            lock (workerNonces)
            {
                RegisterNonce(worker, nonce);
            }

            var context      = worker.GetContextAs <EthereumWorkerContext>();
            var fullNonceHex = context.ExtraNonce1 + nonce;

            if (!ulong.TryParse(fullNonceHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var fullNonce))
            {
                throw new StratumException(StratumError.MinusOne, "bad nonce " + fullNonceHex);
            }

            var dag = await ethash.GetDagAsync(BlockTemplate.Height, logger);

            if (!dag.Compute(logger, BlockTemplate.Header.HexToByteArray(), fullNonce, out var mixDigest, out var resultBytes))
            {
                throw new StratumException(StratumError.MinusOne, "bad hash");
            }

            resultBytes.ReverseArray();

            var resultValue       = new uint256(resultBytes);
            var resultValueBig    = resultBytes.ToBigInteger();
            var shareDiff         = (double)BigInteger.Divide(EthereumConstants.BigMaxValue, resultValueBig) / EthereumConstants.Pow2x32;
            var stratumDifficulty = context.Difficulty;
            var ratio             = shareDiff / stratumDifficulty;
            var isBlockCandidate  = resultValue <= blockTarget;

            if (!isBlockCandidate && ratio < 0.99)
            {
                if (context.VarDiff?.LastUpdate != null && context.PreviousDifficulty.HasValue)
                {
                    ratio = shareDiff / context.PreviousDifficulty.Value;

                    if (ratio < 0.99)
                    {
                        throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})");
                    }

                    stratumDifficulty = context.PreviousDifficulty.Value;
                }

                else
                {
                    throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})");
                }
            }

            var share = new Share
            {
                BlockHeight      = (long)BlockTemplate.Height,
                IpAddress        = worker.RemoteEndpoint?.Address?.ToString(),
                Miner            = context.MinerName,
                Worker           = context.WorkerName,
                UserAgent        = context.UserAgent,
                IsBlockCandidate = isBlockCandidate,
                Difficulty       = stratumDifficulty * EthereumConstants.Pow2x32,
                BlockHash        = mixDigest.ToHexString(true)
            };

            if (share.IsBlockCandidate)
            {
                fullNonceHex = "0x" + fullNonceHex;
                var headerHash = BlockTemplate.Header;
                var mixHash    = mixDigest.ToHexString(true);

                share.TransactionConfirmationData = $"{mixDigest.ToHexString(true)}:{fullNonceHex}";

                return(share, fullNonceHex, headerHash, mixHash);
            }

            return(share, null, null, null);
        }
        public async Task <(Share Share, string FullNonceHex, string HeaderHash, string MixHash)> ProcessShareAsync(StratumClient worker, string nonce, EthashFull ethash)
        {
            // duplicate nonce?
            //lock (workerNonces)
            //{
            //    RegisterNonce(worker, nonce);
            //}

            // assemble full-nonce
            var context      = worker.GetContextAs <EthereumWorkerContext>();
            var fullNonceHex = nonce.StartsWith("0x") ? nonce.Substring(2) : nonce;

            if (context.IsNiceHashClient && !string.IsNullOrEmpty(context.ExtraNonce1))
            {
                fullNonceHex = context.ExtraNonce1 + fullNonceHex;
            }

            if (!ulong.TryParse(fullNonceHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var fullNonce))
            {
                throw new StratumException(StratumError.Other, "bad nonce " + fullNonceHex);
            }

            // get dag for block
            var dag = await ethash.GetDagAsync(BlockTemplate.Height, logger);

            // compute
            if (!dag.Compute(logger, BlockTemplate.Header.HexToByteArray(), fullNonce, out var mixDigest, out var resultBytes))
            {
                throw new StratumException(StratumError.Other, "bad hash");
            }

            resultBytes.ReverseArray();

            // test if share meets at least workers current difficulty
            var resultValue       = new uint256(resultBytes);
            var resultValueBig    = resultBytes.ToBigInteger();
            var shareDiff         = (double)BigInteger.Divide(EthereumConstants.BigMaxValue, resultValueBig);
            var stratumDifficulty = context.Difficulty * EthereumConstants.StratumDiffFactor;
            var ratio             = shareDiff / stratumDifficulty;
            var isBlockCandidate  = resultValue <= blockTarget;

            if (!isBlockCandidate && ratio < 0.98)
            {
                throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})");
            }

            // create share
            var share = new Share
            {
                BlockHeight      = (long)BlockTemplate.Height,
                IpAddress        = worker.RemoteEndpoint?.Address?.ToString(),
                Miner            = context.MinerName,
                Worker           = context.WorkerName,
                UserAgent        = context.UserAgent,
                IsBlockCandidate = isBlockCandidate,
                Difficulty       = stratumDifficulty,
                BlockHash        = mixDigest.ToHexString(true)
            };

            if (share.IsBlockCandidate)
            {
                var headerHash = BlockTemplate.Header;
                var mixHash    = mixDigest.ToHexString(true);

                share.TransactionConfirmationData = $"{mixDigest.ToHexString(true)}:{nonce}";

                return(share, fullNonceHex, headerHash, mixHash);
            }

            return(share, null, null, null);
        }