protected virtual (Share Share, string BlockHex) ProcessShareInternal(StratumClient worker, string nonce, uint nTime, string solution) { var context = worker.ContextAs <BitcoinWorkerContext>(); var solutionBytes = solution.HexToByteArray(); // serialize block-header var headerBytes = SerializeHeader(nTime, nonce); // 144 bytes (doesn't contain soln) // verify solution if (!equihash.Verify(headerBytes, solutionBytes.Skip(chainConfig.SolutionPreambleSize).ToArray())) // skip preamble (3 bytes) { throw new StratumException(StratumError.Other, "invalid solution"); } // hash block-header var headerSolutionBytes = headerBytes.Concat(solutionBytes).ToArray(); Span <byte> headerHash = stackalloc byte[32]; headerHasher.Digest(headerSolutionBytes, headerHash, (ulong)nTime); var headerValue = new uint256(headerHash); // calc share-diff var shareDiff = (double)new BigRational(chainConfig.Diff1b, headerHash.ToBigInteger()) * shareMultiplier; var stratumDifficulty = 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 (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})"); } } var result = new Share { BlockHeight = BlockTemplate.Height, NetworkDifficulty = Difficulty, Difficulty = stratumDifficulty, }; if (isBlockCandidate) { var headerHashReversed = headerHash.ToNewReverseArray(); result.IsBlockCandidate = true; result.BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC); result.BlockHash = headerHashReversed.ToHexString(); var blockBytes = SerializeBlock(headerBytes, coinbaseInitial, solutionBytes); var blockHex = blockBytes.ToHexString(); return(result, blockHex); } return(result, null); }
protected virtual (Share Share, string BlockHex) ProcessShareInternal(StratumClient worker, string extraNonce, uint nTime, string nonce, string solution) { var context = worker.ContextAs <BitcoinWorkerContext>(); var solutionBytes = solution.HexToByteArray(); var extraNonceBytes = extraNonce.HexToByteArray(); var nonceInt = uint.Parse(nonce, NumberStyles.HexNumber); // serialize block-header var headerBytes = SerializeHeader(nTime, extraNonceBytes, nonceInt); // verify solution if (!equihash.Verify(headerBytes, solutionBytes)) { throw new StratumException(StratumError.Other, "invalid solution"); } // hash block-header var headerSolutionBytes = headerBytes.Concat(solutionBytes).ToArray(); var headerHash = headerHasher.Digest(headerSolutionBytes); var headerValue = new uint256(headerHash); // calc share-diff double shareDiff = (double)new BigRational(chainConfig.Diff1b, headerHash.ToBigInteger()); var stratumDifficulty = context.Difficulty; var ratio = shareDiff / stratumDifficulty; // check if the share meets the much harder block difficulty (block candidate) var isBlockCandidate = headerValue < blockTarget.ToUInt256(); // 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 (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})"); } } var result = new Share { BlockHeight = BlockHeader.Height, NetworkDifficulty = Difficulty, Difficulty = stratumDifficulty, }; if (isBlockCandidate) { result.IsBlockCandidate = true; result.BlockHash = headerValue.ToString(); var blockHex = headerSolutionBytes.ToHexString(); return(result, blockHex); } return(result, null); }