Пример #1
0
        public CryptonoteJob(GetBlockTemplateResponse blockTemplate, byte[] instanceId, string jobId,
                             PoolConfig poolConfig, ClusterConfig clusterConfig, string prevHash)
        {
            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");

            coin          = poolConfig.Template.As <CryptonoteCoinTemplate>();
            BlockTemplate = blockTemplate;
            PrepareBlobTemplate(instanceId);
            PrevHash = prevHash;

            switch (coin.Hash)
            {
            case CryptonightHashType.Normal:
                hashFunc = LibCryptonight.Cryptonight;
                break;

            case CryptonightHashType.Lite:
                hashFunc = LibCryptonight.CryptonightLight;
                break;

            case CryptonightHashType.Heavy:
                hashFunc = LibCryptonight.CryptonightHeavy;
                break;
            }
        }
Пример #2
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);
            var coin = poolConfig.Template.As <CryptonoteCoinTemplate>();

            switch (networkType)
            {
            case CryptonoteNetworkType.Main:
                if (addressPrefix != coin.AddressPrefix &&
                    addressIntegratedPrefix != coin.AddressPrefixIntegrated)
                {
                    return(false);
                }
                break;

            case CryptonoteNetworkType.Test:
                if (addressPrefix != coin.AddressPrefixTestnet &&
                    addressIntegratedPrefix != coin.AddressPrefixIntegratedTestnet)
                {
                    return(false);
                }
                break;
            }

            return(true);
        }
Пример #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>(ILogger logger, 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(logger, endPoint, method, payload, CancellationToken.None, payloadJsonSerializerSettings)).ToArray();

            try
            {
                await Task.WhenAll(tasks);
            }

            catch (Exception)
            {
                // ignored
            }

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

            return(results);
        }
Пример #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 =>
            {
                if (string.IsNullOrEmpty(endpoint.User) || !endpoint.DigestAuth)
                {
                    return(defaultHttpClient);
                }

                return(new HttpClient(new SocketsHttpHandler
                {
                    AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,

                    Credentials = new NetworkCredential(endpoint.User, endpoint.Password),
                    PreAuthenticate = true,

                    SslOptions = new SslClientAuthenticationOptions
                    {
                        RemoteCertificateValidationCallback = ((sender, certificate, chain, errors) => true),
                    }
                }));
            });
        }
Пример #5
0
        public virtual (Share Share, string BlockHex) ProcessShare(StratumClient worker,
                                                                   string extraNonce2, string nTime, string nonce, string versionBits = null)
        {
            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.ContextAs <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);

            // validate version-bits (overt ASIC boost)
            uint versionBitsInt = 0;

            if (context.VersionRollingMask.HasValue && versionBits != null)
            {
                versionBitsInt = uint.Parse(versionBits, NumberStyles.HexNumber);

                // enforce that only bits covered by current mask are changed by miner
                if ((versionBitsInt & ~context.VersionRollingMask.Value) != 0)
                {
                    throw new StratumException(StratumError.Other, "rolling-version mask violation");
                }
            }

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

            return(ProcessShareInternal(worker, extraNonce2, nTimeInt, nonceInt, versionBitsInt));
        }
Пример #6
0
        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);
        }
Пример #7
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);
        }
Пример #8
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>(ILogger logger, CancellationToken ct, 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 task = BuildRequestTask(logger, endPoints.First(), method, payload, ct, payloadJsonSerializerSettings);
            await Task.WhenAny(new[] { task });

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

            return(result);
        }
Пример #9
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
            };
        }
Пример #10
0
        /// <summary>
        /// Executes the request against all configured demons and returns the first successful response
        /// </summary>
        /// <typeparam name="TResponse"></typeparam>
        /// <returns></returns>
        public async Task <DaemonResponse <TResponse> > ExecuteCmdAnyAsync <TResponse>(ILogger logger, CancellationToken ct, string method, object payload = null,
                                                                                       JsonSerializerSettings payloadJsonSerializerSettings = null, bool throwOnError = false)
            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(logger, endPoint, method, payload, ct, payloadJsonSerializerSettings)).ToArray();

            var taskFirstCompleted = await Task.WhenAny(tasks);

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

            return(result);
        }
Пример #11
0
        public void Init(BlockTemplate blockTemplate, string jobId,
                         PoolConfig poolConfig, BitcoinPoolConfigExtra extraPoolConfig,
                         ClusterConfig clusterConfig, IMasterClock clock,
                         IDestination poolAddressDestination, Network network,
                         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;
            coin                        = poolConfig.Template.As <BitcoinTemplate>();
            networkParams               = coin.GetNetwork(network.NetworkType);
            txVersion                   = coin.CoinbaseTxVersion;
            this.network                = network;
            this.clock                  = clock;
            this.poolAddressDestination = poolAddressDestination;
            BlockTemplate               = blockTemplate;
            JobId                       = jobId;
            Difficulty                  = new Target(new NBitcoin.BouncyCastle.Math.BigInteger(BlockTemplate.Target, 16)).Difficulty;
            extraNoncePlaceHolderLength = BitcoinConstants.ExtranoncePlaceHolderLength;
            this.isPoS                  = isPoS;
            this.shareMultiplier        = shareMultiplier;

            txComment = !string.IsNullOrEmpty(extraPoolConfig?.CoinbaseTxComment) ?
                        extraPoolConfig.CoinbaseTxComment : coin.CoinbaseTxComment;

            if (coin.HasMasterNodes)
            {
                masterNodeParameters = BlockTemplate.Extra.SafeExtensionDataAs <MasterNodeBlockTemplateExtra>();

                if (!string.IsNullOrEmpty(masterNodeParameters.CoinbasePayload))
                {
                    txVersion = 3;
                    var txType = 5;
                    txVersion = txVersion + ((uint)(txType << 16));
                }
            }
            if (coin.HasCoinbasePayload)
            {
                coinbasepayloadParameters = BlockTemplate.Extra.SafeExtensionDataAs <CoinbasePayloadBlockTemplateExtra>();
            }

            if (coin.HasFounderFee)
            {
                FounderParameters = BlockTemplate.Extra.SafeExtensionDataAs <FounderBlockTemplateExtra>();
            }

            if (coin.HasCoinbaseDevReward)
            {
                CoinbaseDevRewardParams = BlockTemplate.Extra.SafeExtensionDataAs <CoinbaseDevRewardTemplateExtra>();
            }

            if (coin.HasTreasuryReward)
            {
                TreasuryParameters = BlockTemplate.Extra.SafeExtensionDataAs <TreasuryBlockTemplateExtra>();
            }

            if (coin.HasPayee)
            {
                payeeParameters = BlockTemplate.Extra.SafeExtensionDataAs <PayeeBlockTemplateExtra>();
            }

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

            if (!string.IsNullOrEmpty(BlockTemplate.Target))
            {
                blockTargetValue = new uint256(BlockTemplate.Target);
            }
            else
            {
                var tmp = new Target(BlockTemplate.Bits.HexToByteArray());
                blockTargetValue = tmp.ToUInt256();
            }

            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
            };
        }
Пример #12
0
        public (Share Share, string BlobHex) 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 <CryptonoteWorkerContext>();

            // validate nonce
            if (!CryptonoteConstants.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(CryptonoteConstants.BlobNonceOffset, nonceBytes.Length));

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

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

            // determine variant
            CryptonightVariant variant = CryptonightVariant.VARIANT_0;

            if (coin.HashVariant != 0)
            {
                variant = (CryptonightVariant)coin.HashVariant;
            }
            else
            {
                switch (coin.Hash)
                {
                case CryptonightHashType.Normal:
                    variant = (blobConverted[0] >= 10) ? CryptonightVariant.VARIANT_4 :
                              ((blobConverted[0] >= 8) ? CryptonightVariant.VARIANT_2 :
                               ((blobConverted[0] == 7) ? CryptonightVariant.VARIANT_1 :
                                CryptonightVariant.VARIANT_0));
                    break;

                case CryptonightHashType.Lite:
                    variant = CryptonightVariant.VARIANT_1;
                    break;

                case CryptonightHashType.Heavy:
                    variant = CryptonightVariant.VARIANT_0;
                    break;
                }
            }

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

            hashFunc(blobConverted, headerHash, variant, BlockTemplate.Height);

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

            var result = new Share
            {
                BlockHeight = BlockTemplate.Height,
                Difficulty  = stratumDifficulty,
            };

            if (isBlockCandidate)
            {
                // Compute block hash
                Span <byte> blockHash = stackalloc byte[32];
                ComputeBlockHash(blobConverted, blockHash);

                // Fill in block-relevant fields
                result.IsBlockCandidate = true;
                result.BlockHash        = blockHash.ToHexString();
            }

            return(result, blob.ToHexString());
        }