private async Task <bool> SubmitBlockAsync(MoneroShare share) { var response = await daemon.ExecuteCmdAnyAsync <SubmitResponse>(MC.SubmitBlock, new[] { share.BlobHex }); if (response.Error != null || response?.Response?.Status != "OK") { var error = response.Error?.Message ?? response.Response?.Status; logger.Warn(() => $"[{LogCat}] Block {share.BlockHeight} [{share.BlobHash.Substring(0, 6)}] submission failed with: {error}"); return(false); } return(true); }
public MoneroShare ProcessShare(string nonce, uint workerExtraNonce, string workerHash, StratumClient <MoneroWorkerContext> worker) { Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(nonce), $"{nameof(nonce)} must not be empty"); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(workerHash), $"{nameof(workerHash)} must not be empty"); Contract.Requires <ArgumentException>(workerExtraNonce != 0, $"{nameof(workerExtraNonce)} must not be empty"); // validate nonce if (!MoneroConstants.RegexValidNonce.IsMatch(nonce)) { throw new StratumException(StratumError.MinusOne, "malformed nonce"); } // clone template var blob = new byte[blobTemplate.Length]; Buffer.BlockCopy(blobTemplate, 0, blob, 0, blobTemplate.Length); // inject extranonce var extraNonceBytes = BitConverter.GetBytes(workerExtraNonce.ToBigEndian()); Buffer.BlockCopy(extraNonceBytes, 0, blob, (int)BlockTemplate.ReservedOffset, extraNonceBytes.Length); // inject nonce var nonceBytes = nonce.HexToByteArray(); Buffer.BlockCopy(nonceBytes, 0, blob, MoneroConstants.BlobNonceOffset, nonceBytes.Length); // convert var blobConverted = LibCryptonote.ConvertBlob(blob); if (blobConverted == null) { throw new StratumException(StratumError.MinusOne, "malformed blob"); } // hash it var hashBytes = LibCryptonote.CryptonightHashSlow(blobConverted); var hash = hashBytes.ToHexString(); if (hash != workerHash) { throw new StratumException(StratumError.MinusOne, "bad hash"); } // check difficulty var headerValue = System.Numerics.BigInteger.Parse("0" + hashBytes.ToReverseArray().ToHexString(), NumberStyles.HexNumber); var shareDiff = (double)new BigRational(MoneroConstants.Diff1b, headerValue); var stratumDifficulty = worker.Context.Difficulty; var ratio = shareDiff / stratumDifficulty; var isBlockCandidate = shareDiff >= BlockTemplate.Difficulty; // 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 MoneroShare { BlockHeight = BlockTemplate.Height, IsBlockCandidate = isBlockCandidate, BlobHex = blob.ToHexString(), BlobHash = ComputeBlockHash(blobConverted).ToHexString(), StratumDifficulty = stratumDifficulty, }; return(result); }
public MoneroShare ProcessShare(string nonce, uint workerExtraNonce, string workerHash, double stratumDifficulty) { Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(nonce), $"{nameof(nonce)} must not be empty"); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(workerHash), $"{nameof(workerHash)} must not be empty"); Contract.Requires <ArgumentException>(extraNonce != 0, $"{nameof(extraNonce)} must not be empty"); // validate nonce if (!MoneroConstants.RegexValidNonce.IsMatch(nonce)) { throw new StratumException(StratumError.MinusOne, "malformed nonce"); } // clone template var blob = new byte[blobTemplate.Length]; Buffer.BlockCopy(blobTemplate, 0, blob, 0, blobTemplate.Length); // inject extranonce var extraNonceBytes = BitConverter.GetBytes(workerExtraNonce.ToBigEndian()); Buffer.BlockCopy(extraNonceBytes, 0, blob, (int)BlockTemplate.ReservedOffset, extraNonceBytes.Length); // inject nonce var nonceBytes = nonce.HexToByteArray(); Buffer.BlockCopy(nonceBytes, 0, blob, MoneroConstants.BlobNonceOffset, nonceBytes.Length); // convert var blobConverted = LibCryptonote.ConvertBlob(blob); if (blobConverted == null) { throw new StratumException(StratumError.MinusOne, "malformed blob"); } // hash it var hashBytes = LibCryptonote.CryptonightHashSlow(blobConverted); var hash = hashBytes.ToHexString(); if (hash != workerHash) { throw new StratumException(StratumError.MinusOne, "bad hash"); } // check difficulty var headerValue = new System.Numerics.BigInteger(hashBytes); var shareDiff = (double)new BigRational(MoneroConstants.Diff1b, headerValue); 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 = shareDiff >= BlockTemplate.Difficulty; var result = new MoneroShare { BlockHeight = BlockTemplate.Height, IsBlockCandidate = isBlockCandidate, BlobHex = blob.ToHexString(), BlobHash = ComputeBlockHash(blobConverted).ToHexString() }; return(result); }