protected virtual BitcoinShare ProcessShareInternal(StratumClient <BitcoinWorkerContext> worker, string extraNonce2, uint nTime, uint nonce) { var extraNonce1 = worker.Context.ExtraNonce1; // build coinbase var coinbase = SerializeCoinbase(extraNonce1, extraNonce2); var coinbaseHash = coinbaseHasher.Digest(coinbase); // hash block-header var headerBytes = SerializeHeader(coinbaseHash, nTime, nonce); var headerHash = headerHasher.Digest(headerBytes, (ulong)nTime); var headerValue = BigInteger.Parse("00" + headerHash.ReverseArray().ToHexString(), NumberStyles.HexNumber); // calc share-diff var shareDiff = (double)new BigRational(BitcoinConstants.Diff1, headerValue) * shareMultiplier; var stratumDifficulty = worker.Context.Difficulty; var ratio = shareDiff / stratumDifficulty; // check if the share meets the much harder block difficulty (block candidate) var isBlockCandidate = headerValue < blockTargetValue; // test if share meets at least workers current difficulty if (!isBlockCandidate && ratio < 0.99) { // check if share matched the previous difficulty from before a vardiff retarget if (worker.Context.VarDiff?.LastUpdate != null && worker.Context.PreviousDifficulty.HasValue) { ratio = shareDiff / worker.Context.PreviousDifficulty.Value; if (ratio < 0.99) { throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})"); } // use previous difficulty stratumDifficulty = worker.Context.PreviousDifficulty.Value; } else { throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})"); } } var result = new BitcoinShare { BlockHeight = BlockTemplate.Height, IsBlockCandidate = isBlockCandidate }; var blockBytes = SerializeBlock(headerBytes, coinbase); result.BlockHex = blockBytes.ToHexString(); result.BlockHash = blockHasher.Digest(headerBytes, nTime).ToHexString(); result.BlockHeight = BlockTemplate.Height; result.BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC); result.Difficulty = stratumDifficulty; return(result); }
protected virtual BitcoinShare ProcessShareInternal(string extraNonce1, string extraNonce2, uint nTime, uint nonce, double stratumDifficulty) { // build coinbase var coinbase = SerializeCoinbase(extraNonce1, extraNonce2); var coinbaseHash = coinbaseHasher.Digest(coinbase); // build merkle-root var merkleRoot = mt.WithFirst(coinbaseHash) .ToArray(); // build block-header var blockHeader = new BlockHeader { Version = (int)BlockTemplate.Version, Bits = new Target(Encoders.Hex.DecodeData(BlockTemplate.Bits)), HashPrevBlock = uint256.Parse(BlockTemplate.PreviousBlockhash), HashMerkleRoot = new uint256(merkleRoot), BlockTime = DateTimeOffset.FromUnixTimeSeconds(nTime), Nonce = nonce }; // hash block-header var headerBytes = blockHeader.ToBytes(); var headerHash = headerHasher.Digest(headerBytes, (ulong)nTime); var headerValue = new BigInteger(headerHash); // calc share-diff var shareDiff = (double)new BigRational(BitcoinConstants.Diff1, headerValue) * shareMultiplier; var ratio = shareDiff / stratumDifficulty; // test if share meets at least workers current difficulty if (ratio < 0.99) { throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})"); } // valid share, check if the share also meets the much harder block difficulty (block candidate) var isBlockCandidate = headerValue < blockTargetValue; var result = new BitcoinShare { BlockHeight = BlockTemplate.Height, IsBlockCandidate = isBlockCandidate }; var blockBytes = SerializeBlock(headerBytes, coinbase); result.BlockHex = blockBytes.ToHexString(); result.BlockHash = blockHasher.Digest(headerBytes, nTime).ToHexString(); result.BlockHeight = BlockTemplate.Height; result.BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC); return(result); }
private async Task <(bool Accepted, string CoinbaseTransaction)> SubmitBlockAsync(BitcoinShare share) { // execute command batch var results = await daemon.ExecuteBatchAnyAsync( hasSubmitBlockMethod ?new DaemonCmd(BitcoinCommands.SubmitBlock, new[] { share.BlockHex }) : new DaemonCmd(BitcoinCommands.GetBlockTemplate, new { mode = "submit", data = share.BlockHex }), new DaemonCmd(BitcoinCommands.GetBlock, new[] { share.BlockHash })); // did submission succeed? var submitResult = results[0]; var submitError = submitResult.Error?.Message ?? submitResult.Response?.ToString(); if (!string.IsNullOrEmpty(submitError)) { logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed with: {submitError}"); return(false, null); } // was it accepted? var acceptResult = results[1]; var block = acceptResult.Response?.ToObject <Blocks>(); var accepted = acceptResult.Error == null && block?.Hash == share.BlockHash; return(accepted, block?.Transactions.FirstOrDefault()); }
protected virtual async Task <(bool Accepted, string CoinbaseTransaction)> SubmitBlockAsync(BitcoinShare share) { // execute command batch var results = await daemon.ExecuteBatchAnyAsync( hasSubmitBlockMethod ?new DaemonCmd(BitcoinCommands.SubmitBlock, new[] { share.BlockHex }) : new DaemonCmd(BitcoinCommands.GetBlockTemplate, new { mode = "submit", data = share.BlockHex }), new DaemonCmd(BitcoinCommands.GetBlock, new[] { share.BlockHash })); // did submission succeed? var submitResult = results[0]; var submitError = submitResult.Error?.Message ?? submitResult.Response?.ToString(); if (!string.IsNullOrEmpty(submitError)) { logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed with: {submitError}"); notificationService.NotifyAdmin("Block submission failed", $"Block {share.BlockHeight} submission failed with: {submitError}"); return(false, null); } // was it accepted? var acceptResult = results[1]; var block = acceptResult.Response?.ToObject <DaemonResponses.Block>(); var accepted = acceptResult.Error == null && block?.Hash == share.BlockHash; if (!accepted) { logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission"); notificationService.NotifyAdmin("Block submission failed", $"Block {share.BlockHeight} submission failed for pool {poolConfig.Id} because block was not found after submission"); } return(accepted, block?.Transactions.FirstOrDefault()); }