Esempio n. 1
0
        public MoneroJob(GetBlockTemplateResponse blockTemplate, byte[] instanceId, string jobId,
                         PoolConfig poolConfig, ClusterConfig clusterConfig)
        {
            Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate));
            Contract.RequiresNonNull(poolConfig, nameof(poolConfig));
            Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig));
            Contract.RequiresNonNull(instanceId, nameof(instanceId));
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty");

            switch (poolConfig.Coin.Type)
            {
            case CoinType.AEON:
                hashSlow = LibCryptonote.CryptonightHashSlowLite;
                break;

            case CoinType.XMR:
                hashSlow = buf =>
                {
                    // PoW variant
                    var variant = buf[0] >= 7 ? buf[0] - 6 : 0;

                    return(LibCryptonote.CryptonightHashSlow(buf, variant));
                };
                break;

            default:
                hashSlow = buf => LibCryptonote.CryptonightHashSlow(buf, 0);
                break;
            }

            BlockTemplate = blockTemplate;
            PrepareBlobTemplate(instanceId);
        }
Esempio n. 2
0
        public void Ban(IPAddress address, TimeSpan duration)
        {
            Contract.RequiresNonNull(address, nameof(address));
            Contract.Requires <ArgumentException>(duration.TotalMilliseconds > 0, $"{nameof(duration)} must not be empty");

            cache.Set(address.ToString(), string.Empty, duration);
        }
Esempio n. 3
0
        /// <summary>
        /// Executes the request against all configured demons and returns their responses as an array
        /// </summary>
        /// <typeparam name="TResponse"></typeparam>
        /// <param name="method"></param>
        /// <param name="payload"></param>
        /// <returns></returns>
        public async Task <DaemonResponse <TResponse>[]> ExecuteCmdAllAsync <TResponse>(string method,
                                                                                        object payload = null, JsonSerializerSettings payloadJsonSerializerSettings = null)
            where TResponse : class
        {
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty");

            logger.LogInvoke(new[] { method });

            var tasks = endPoints.Select(endPoint => BuildRequestTask(endPoint, method, payload, payloadJsonSerializerSettings)).ToArray();

            try
            {
                await Task.WhenAll(tasks);
            }

            catch (Exception)
            {
                // ignored
            }

            var results = tasks.Select((x, i) => MapDaemonResponse <TResponse>(i, x))
                          .ToArray();

            return(results);
        }
Esempio n. 4
0
        public void Configure(DaemonEndpointConfig[] endPoints, string digestAuthRealm = null)
        {
            Contract.RequiresNonNull(endPoints, nameof(endPoints));
            Contract.Requires <ArgumentException>(endPoints.Length > 0, $"{nameof(endPoints)} must not be empty");

            this.endPoints = endPoints;

            // create one HttpClient instance per endpoint that carries the associated credentials
            httpClients = endPoints.ToDictionary(endpoint => endpoint, endpoint =>
            {
                var handler = new HttpClientHandler
                {
                    Credentials            = new NetworkCredential(endpoint.User, endpoint.Password),
                    PreAuthenticate        = true,
                    AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
                };

                if (endpoint.Ssl && !endpoint.ValidateCert)
                {
                    handler.ClientCertificateOptions = ClientCertificateOption.Manual;
                    handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true;
                }

                return(new HttpClient(handler));
            });
        }
Esempio n. 5
0
        public void RespondError(object id, int code, string message)
        {
            Contract.RequiresNonNull(id, nameof(id));
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(message), $"{nameof(message)} must not be empty");

            Respond(new JsonRpcResponse(new JsonRpcException(code, message, null), id));
        }
Esempio n. 6
0
        public bool ValidateAddress(string address)
        {
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty");

            var addressPrefix           = LibCryptonote.DecodeAddress(address);
            var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address);

            switch (networkType)
            {
            case MoneroNetworkType.Main:
                if (addressPrefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type] &&
                    addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type])
                {
                    return(false);
                }
                break;

            case MoneroNetworkType.Test:
                if (addressPrefix != MoneroConstants.AddressPrefixTestnet[poolConfig.Coin.Type] &&
                    addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type])
                {
                    return(false);
                }
                break;
            }

            return(true);
        }
Esempio n. 7
0
        public virtual void Init(TBlockTemplate blockTemplate, string jobId,
                                 PoolConfig poolConfig, ClusterConfig clusterConfig, IMasterClock clock,
                                 IDestination poolAddressDestination, BitcoinNetworkType networkType,
                                 bool isPoS, double shareMultiplier,
                                 IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, IHashAlgorithm blockHasher)
        {
            Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate));
            Contract.RequiresNonNull(poolConfig, nameof(poolConfig));
            Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig));
            Contract.RequiresNonNull(clock, nameof(clock));
            Contract.RequiresNonNull(poolAddressDestination, nameof(poolAddressDestination));
            Contract.RequiresNonNull(coinbaseHasher, nameof(coinbaseHasher));
            Contract.RequiresNonNull(headerHasher, nameof(headerHasher));
            Contract.RequiresNonNull(blockHasher, nameof(blockHasher));
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty");

            this.poolConfig             = poolConfig;
            this.clusterConfig          = clusterConfig;
            this.clock                  = clock;
            this.poolAddressDestination = poolAddressDestination;
            this.networkType            = networkType;
            BlockTemplate               = blockTemplate;
            JobId      = jobId;
            Difficulty = new Target(new NBitcoin.BouncyCastle.Math.BigInteger(BlockTemplate.Target, 16)).Difficulty;

            extraNoncePlaceHolderLength = BitcoinExtraNonceProvider.PlaceHolderLength;
            this.isPoS           = isPoS;
            this.shareMultiplier = shareMultiplier;

            this.coinbaseHasher = coinbaseHasher;
            this.headerHasher   = headerHasher;
            this.blockHasher    = blockHasher;

            blockTargetValue = BigInteger.Parse(BlockTemplate.Target, NumberStyles.HexNumber);

            previousBlockHashReversedHex = BlockTemplate.PreviousBlockhash
                                           .HexToByteArray()
                                           .ReverseByteOrder()
                                           .ToHexString();

            BuildMerkleBranches();
            BuildCoinbase();

            jobParams = new object[]
            {
                JobId,
                previousBlockHashReversedHex,
                coinbaseInitialHex,
                coinbaseFinalHex,
                merkleBranchesHex,
                BlockTemplate.Version.ToStringHex8(),
                BlockTemplate.Bits,
                BlockTemplate.CurTime.ToStringHex8(),
                false
            };
        }
Esempio n. 8
0
        public IObservable <PooledArraySegment <byte>[]> ZmqSubscribe(Dictionary <DaemonEndpointConfig, string> portMap, string topic, int numMsgSegments = 2)
        {
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(topic), $"{nameof(topic)} must not be empty");

            logger.LogInvoke(new[] { topic });

            return(Observable.Merge(portMap.Keys
                                    .Select(endPoint => ZmqSubscribeEndpoint(endPoint, portMap[endPoint], topic, numMsgSegments)))
                   .Publish()
                   .RefCount());
        }
Esempio n. 9
0
        public MoneroJob(GetBlockTemplateResponse blockTemplate, byte[] instanceId, string jobId,
                         PoolConfig poolConfig, ClusterConfig clusterConfig)
        {
            Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate));
            Contract.RequiresNonNull(poolConfig, nameof(poolConfig));
            Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig));
            Contract.RequiresNonNull(instanceId, nameof(instanceId));
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty");

            BlockTemplate = blockTemplate;
            PrepareBlobTemplate(instanceId);
        }
Esempio n. 10
0
        public IObservable <PooledArraySegment <byte> > WebsocketSubscribe(Dictionary <DaemonEndpointConfig, int> portMap, string method, object payload = null,
                                                                           JsonSerializerSettings payloadJsonSerializerSettings = null)
        {
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty");

            logger.LogInvoke(new[] { method });

            return(Observable.Merge(portMap.Keys
                                    .Select(endPoint => WebsocketSubscribeEndpoint(endPoint, portMap[endPoint], method, payload, payloadJsonSerializerSettings)))
                   .Publish()
                   .RefCount());
        }
        public bool ValidateAddress(string address)
        {
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty");

            if (EthereumConstants.ZeroHashPattern.IsMatch(address) ||
                !EthereumConstants.ValidAddressPattern.IsMatch(address))
            {
                return(false);
            }

            return(true);
        }
Esempio n. 12
0
        public bool ValidateAddress(string address)
        {
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty");

            var addressBytes = address.HexToByteArray();

            if (addressBytes.Length != EthereumConstants.AddressLength)
            {
                return(false);
            }

            return(true);
        }
Esempio n. 13
0
        /// <summary>
        /// Executes the request against all configured demons and returns the first successful response
        /// </summary>
        /// <typeparam name="TResponse"></typeparam>
        /// <param name="method"></param>
        /// <param name="payload"></param>
        /// <returns></returns>
        public async Task <DaemonResponse <TResponse> > ExecuteCmdSingleAsync <TResponse>(string method, object payload = null,
                                                                                          JsonSerializerSettings payloadJsonSerializerSettings = null)
            where TResponse : class
        {
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty");

            var   task = BuildRequestTask(endPoints.First(), method, payload, payloadJsonSerializerSettings);
            await task;

            var result = MapDaemonResponse <TResponse>(0, task);

            return(result);
        }
Esempio n. 14
0
        public void Ban(IPAddress address, TimeSpan duration)
        {
            Contract.RequiresNonNull(address, nameof(address));
            Contract.Requires <ArgumentException>(duration.TotalMilliseconds > 0, $"{nameof(duration)} must not be empty");

            // don't ban 127.0.0.1
            if (address.Equals(IPAddress.Loopback) || address.Equals(IPAddress.IPv6Loopback))
            {
                return;
            }

            cache.Set(address.ToString(), string.Empty, duration);
        }
Esempio n. 15
0
        /// <summary>
        /// Executes the request against all configured demons and returns the first successful response
        /// </summary>
        /// <typeparam name="TResponse"></typeparam>
        /// <param name="method"></param>
        /// <param name="payload"></param>
        /// <returns></returns>
        public async Task <DaemonResponse <TResponse> > ExecuteCmdAnyAsync <TResponse>(string method, object payload = null,
                                                                                       JsonSerializerSettings payloadJsonSerializerSettings = null)
            where TResponse : class
        {
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty");

            var tasks = endPoints.Select(endPoint => BuildRequestTask(endPoint, method, payload, payloadJsonSerializerSettings)).ToArray();

            var taskFirstCompleted = await Task.WhenAny(tasks);

            var result = MapDaemonResponse <TResponse>(0, taskFirstCompleted);

            return(result);
        }
Esempio n. 16
0
        public DaemonClient(JsonSerializerSettings serializerSettings, IMessageBus messageBus, string server, string poolId)
        {
            Contract.RequiresNonNull(serializerSettings, nameof(serializerSettings));
            Contract.RequiresNonNull(messageBus, nameof(messageBus));
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(poolId), $"{nameof(poolId)} must not be empty");

            this.serializerSettings = serializerSettings;
            this.messageBus         = messageBus;
            this.server             = server;
            this.poolId             = poolId;

            serializer = new JsonSerializer
            {
                ContractResolver = serializerSettings.ContractResolver
            };
        }
Esempio n. 17
0
        public bool ValidateAddress(string address)
        {
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty");

            if (address.Length != MoneroConstants.AddressLength)
            {
                return(false);
            }

            var addressPrefix = LibCryptonote.DecodeAddress(address);

            if (addressPrefix != poolAddressBase58Prefix)
            {
                return(false);
            }

            return(true);
        }
Esempio n. 18
0
        public void Configure(DaemonEndpointConfig[] endPoints, string rpcLocation = null,
                              string digestAuthRealm = null)
        {
            Contract.RequiresNonNull(endPoints, nameof(endPoints));
            Contract.Requires <ArgumentException>(endPoints.Length > 0, $"{nameof(endPoints)} must not be empty");

            this.endPoints   = endPoints;
            this.rpcLocation = rpcLocation;

            // create one HttpClient instance per endpoint that carries the associated credentials
            httpClients = endPoints.ToDictionary(endpoint => endpoint, endpoint =>
                                                 new HttpClient(new HttpClientHandler
            {
                Credentials            = new NetworkCredential(endpoint.User, endpoint.Password),
                PreAuthenticate        = true,
                AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
            }));
        }
Esempio n. 19
0
        public void Init(TBlockTemplate blockTemplate, string jobId,
                         PoolConfig poolConfig, ClusterConfig clusterConfig,
                         IDestination poolAddressDestination, BitcoinNetworkType networkType,
                         BitcoinExtraNonceProvider extraNonceProvider, bool isPoS, double shareMultiplier,
                         IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, IHashAlgorithm blockHasher)
        {
            Contract.RequiresNonNull(blockTemplate, nameof(blockTemplate));
            Contract.RequiresNonNull(poolConfig, nameof(poolConfig));
            Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig));
            Contract.RequiresNonNull(poolAddressDestination, nameof(poolAddressDestination));
            Contract.RequiresNonNull(extraNonceProvider, nameof(extraNonceProvider));
            Contract.RequiresNonNull(coinbaseHasher, nameof(coinbaseHasher));
            Contract.RequiresNonNull(headerHasher, nameof(headerHasher));
            Contract.RequiresNonNull(blockHasher, nameof(blockHasher));
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(jobId), $"{nameof(jobId)} must not be empty");

            this.poolConfig             = poolConfig;
            this.clusterConfig          = clusterConfig;
            this.poolAddressDestination = poolAddressDestination;
            this.networkType            = networkType;
            BlockTemplate = blockTemplate;
            JobId         = jobId;

            extraNoncePlaceHolderLength = extraNonceProvider.PlaceHolder.Length;
            this.isPoS           = isPoS;
            this.shareMultiplier = shareMultiplier;

            this.coinbaseHasher = coinbaseHasher;
            this.headerHasher   = headerHasher;
            this.blockHasher    = blockHasher;

            blockTargetValue = BigInteger.Parse(BlockTemplate.Target, NumberStyles.HexNumber);

            previousBlockHashReversedHex = BlockTemplate.PreviousBlockhash
                                           .HexToByteArray()
                                           .ReverseByteOrder()
                                           .ToHexString();

            BuildMerkleBranches();
            BuildCoinbase();
        }
Esempio n. 20
0
        public virtual BitcoinShare ProcessShare(StratumClient worker,
                                                 string extraNonce2, string nTime, string nonce)
        {
            Contract.RequiresNonNull(worker, nameof(worker));
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(extraNonce2), $"{nameof(extraNonce2)} must not be empty");
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(nTime), $"{nameof(nTime)} must not be empty");
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(nonce), $"{nameof(nonce)} must not be empty");

            var context = worker.GetContextAs <BitcoinWorkerContext>();

            // validate nTime
            if (nTime.Length != 8)
            {
                throw new StratumException(StratumError.Other, "incorrect size of ntime");
            }

            var nTimeInt = uint.Parse(nTime, NumberStyles.HexNumber);

            if (nTimeInt < BlockTemplate.CurTime || nTimeInt > ((DateTimeOffset)clock.Now).ToUnixTimeSeconds() + 7200)
            {
                throw new StratumException(StratumError.Other, "ntime out of range");
            }

            // validate nonce
            if (nonce.Length != 8)
            {
                throw new StratumException(StratumError.Other, "incorrect size of nonce");
            }

            var nonceInt = uint.Parse(nonce, NumberStyles.HexNumber);

            // dupe check
            if (!RegisterSubmit(context.ExtraNonce1, extraNonce2, nTime, nonce))
            {
                throw new StratumException(StratumError.DuplicateShare, "duplicate share");
            }

            return(ProcessShareInternal(worker, extraNonce2, nTimeInt, nonceInt));
        }
Esempio n. 21
0
        public virtual BitcoinShare ProcessShare(string extraNonce1, string extraNonce2, string nTime, string nonce, double stratumDifficulty)
        {
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(extraNonce1), $"{nameof(extraNonce1)} must not be empty");
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(extraNonce2), $"{nameof(extraNonce2)} must not be empty");
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(nTime), $"{nameof(nTime)} must not be empty");
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(nonce), $"{nameof(nonce)} must not be empty");

            // validate nTime
            if (nTime.Length != 8)
            {
                throw new StratumException(StratumError.Other, "incorrect size of ntime");
            }

            var nTimeInt = uint.Parse(nTime, NumberStyles.HexNumber);

            if (nTimeInt < BlockTemplate.CurTime || nTimeInt > DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 7200)
            {
                throw new StratumException(StratumError.Other, "ntime out of range");
            }

            // validate nonce
            if (nonce.Length != 8)
            {
                throw new StratumException(StratumError.Other, "incorrect size of nonce");
            }

            var nonceInt = uint.Parse(nonce, NumberStyles.HexNumber);

            // dupe check
            if (!RegisterSubmit(extraNonce1, extraNonce2, nTime, nonce))
            {
                throw new StratumException(StratumError.DuplicateShare, "duplicate share");
            }

            return(ProcessShareInternal(extraNonce1, extraNonce2, nTimeInt, nonceInt, stratumDifficulty));
        }
Esempio n. 22
0
        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);
        }
Esempio n. 23
0
        public (Share Share, string BlobHex, string BlobHash) ProcessShare(string nonce, uint workerExtraNonce, string workerHash, StratumClient 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");

            var context = worker.GetContextAs <MoneroWorkerContext>();

            // validate nonce
            if (!MoneroConstants.RegexValidNonce.IsMatch(nonce))
            {
                throw new StratumException(StratumError.MinusOne, "malformed nonce");
            }

            // clone template
            using (var blob = new PooledArraySegment <byte>(blobTemplate.Length))
            {
                Buffer.BlockCopy(blobTemplate, 0, blob.Array, 0, blobTemplate.Length);

                // inject extranonce
                var extraNonceBytes = BitConverter.GetBytes(workerExtraNonce.ToBigEndian());
                Buffer.BlockCopy(extraNonceBytes, 0, blob.Array, (int)BlockTemplate.ReservedOffset, extraNonceBytes.Length);

                // inject nonce
                var nonceBytes = nonce.HexToByteArray();
                Buffer.BlockCopy(nonceBytes, 0, blob.Array, MoneroConstants.BlobNonceOffset, nonceBytes.Length);

                // convert
                var blobConverted = LibCryptonote.ConvertBlob(blob.Array, blobTemplate.Length);
                if (blobConverted == null)
                {
                    throw new StratumException(StratumError.MinusOne, "malformed blob");
                }

                // hash it
                using (var hashSeg = hashSlow(blobConverted))
                {
                    var hash = hashSeg.ToHexString();
                    if (hash != workerHash)
                    {
                        throw new StratumException(StratumError.MinusOne, "bad hash");
                    }

                    // check difficulty
                    var headerValue       = hashSeg.ToBigInteger();
                    var shareDiff         = (double)new BigRational(MoneroConstants.Diff1b, headerValue);
                    var stratumDifficulty = 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 (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})");
                        }
                    }

                    using (var blockHash = ComputeBlockHash(blobConverted))
                    {
                        var result = new Share
                        {
                            BlockHeight      = BlockTemplate.Height,
                            IsBlockCandidate = isBlockCandidate,
                            BlockHash        = blockHash.ToHexString(),
                            Difficulty       = stratumDifficulty,
                        };

                        var blobHex  = blob.ToHexString();
                        var blobHash = blockHash.ToHexString();

                        return(result, blobHex, blobHash);
                    }
                }
            }
        }
Esempio n. 24
0
        public void Notify <T>(string method, T payload)
        {
            Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty");

            Notify(new JsonRpcRequest <T>(method, payload, null));
        }
Esempio n. 25
0
        public (Share Share, string BlobHex, string BlobHash) ProcessShare(string nonce, uint workerExtraNonce, string workerHash, StratumClient 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");

            var context = worker.ContextAs <MoneroWorkerContext>();

            // validate nonce
            if (!MoneroConstants.RegexValidNonce.IsMatch(nonce))
            {
                throw new StratumException(StratumError.MinusOne, "malformed nonce");
            }

            // clone template
            Span <byte> blob = stackalloc byte[blobTemplate.Length];

            blobTemplate.CopyTo(blob);

            // inject extranonce
            var extraNonceBytes = BitConverter.GetBytes(workerExtraNonce.ToBigEndian());

            extraNonceBytes.CopyTo(blob.Slice(BlockTemplate.ReservedOffset, extraNonceBytes.Length));

            // inject nonce
            var nonceBytes = nonce.HexToByteArray();

            nonceBytes.CopyTo(blob.Slice(MoneroConstants.BlobNonceOffset, nonceBytes.Length));

            // convert
            var blobConverted = LibCryptonote.ConvertBlob(blob, blobTemplate.Length);

            if (blobConverted == null)
            {
                throw new StratumException(StratumError.MinusOne, "malformed blob");
            }

            // hash it
            Span <byte> headerHash = stackalloc byte[32];

            switch (coin)
            {
            case CoinType.AEON:
                LibCryptonight.CryptonightLight(blobConverted, headerHash, 0);
                break;

            case CoinType.XMR:
                var variant = blobConverted[0] >= 7 ? blobConverted[0] - 6 : 0;
                LibCryptonight.Cryptonight(blobConverted, headerHash, variant);
                break;

            default:
                LibCryptonight.Cryptonight(blobConverted, headerHash, 0);
                break;
            }

            var headerHashString = headerHash.ToHexString();

            if (headerHashString != workerHash)
            {
                throw new StratumException(StratumError.MinusOne, "bad hash");
            }

            // check difficulty
            var headerValue       = headerHash.ToBigInteger();
            var shareDiff         = (double)new BigRational(MoneroConstants.Diff1b, headerValue);
            var stratumDifficulty = 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 (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})");
                }
            }

            // Compute block hash
            Span <byte> blockHash = stackalloc byte[32];

            ComputeBlockHash(blobConverted, blockHash);

            var result = new Share
            {
                BlockHeight      = BlockTemplate.Height,
                IsBlockCandidate = isBlockCandidate,
                BlockHash        = blockHash.ToHexString(),
                Difficulty       = stratumDifficulty,
            };

            var blobHex  = blob.ToHexString();
            var blobHash = blockHash.ToHexString();

            return(result, blobHex, blobHash);
        }