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(); }
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(); } }
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); }
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); }
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); }