protected override async Task OnRequestAsync(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { var request = tsRequest.Value; var context = client.ContextAs <MoneroWorkerContext>(); switch (request.Method) { case MoneroStratumMethods.Login: OnLogin(client, tsRequest); break; case MoneroStratumMethods.GetJob: OnGetJob(client, tsRequest); break; case MoneroStratumMethods.Submit: await OnSubmitAsync(client, tsRequest); break; case MoneroStratumMethods.KeepAlive: // recognize activity context.LastActivity = clock.Now; break; default: logger.Debug(() => $"[{LogCat}] [{client.ConnectionId}] Unsupported RPC request: {JsonConvert.SerializeObject(request, serializerSettings)}"); client.RespondError(StratumError.Other, $"Unsupported request {request.Method}", request.Id); break; } }
private async Task OnSuggestDifficultyAsync(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { var request = tsRequest.Value; var context = client.ContextAs <BitcoinWorkerContext>(); // acknowledge await client.RespondAsync(true, request.Id); try { var requestedDiff = (double)Convert.ChangeType(request.Params, TypeCode.Double); // client may suggest higher-than-base difficulty, but not a lower one var poolEndpoint = poolConfig.Ports[client.PoolEndpoint.Port]; if (requestedDiff > poolEndpoint.Difficulty) { context.SetDifficulty(requestedDiff); await client.NotifyAsync(BitcoinStratumMethods.SetDifficulty, new object[] { context.Difficulty }); logger.Info(() => $"[{client.ConnectionId}] Difficulty set to {requestedDiff} as requested by miner"); } } catch (Exception ex) { logger.Error(ex, () => $"Unable to convert suggested difficulty {request.Params}"); } }
private void OnGetJob(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { var request = tsRequest.Value; var context = client.ContextAs <MoneroWorkerContext>(); if (request.Id == null) { client.RespondError(StratumError.MinusOne, "missing request id", request.Id); return; } var getJobRequest = request.ParamsAs <MoneroGetJobRequest>(); // validate worker if (client.ConnectionId != getJobRequest?.WorkerId || !context.IsAuthorized) { client.RespondError(StratumError.MinusOne, "unauthorized", request.Id); return; } // respond var job = CreateWorkerJob(client); client.Respond(job, request.Id); }
private async Task OnConfigureMiningAsync(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { var request = tsRequest.Value; var context = client.ContextAs <BitcoinWorkerContext>(); var requestParams = request.ParamsAs <JToken[]>(); var extensions = requestParams[0].ToObject <string[]>(); var extensionParams = requestParams[1].ToObject <Dictionary <string, JToken> >(); var result = new Dictionary <string, object>(); foreach (var extension in extensions) { switch (extension) { case BitcoinStratumExtensions.VersionRolling: ConfigureVersionRolling(client, context, extensionParams, result); break; case BitcoinStratumExtensions.MinimumDiff: ConfigureMinimumDiff(client, context, extensionParams, result); break; } } await client.RespondAsync(result, request.Id); }
private CryptonoteJobParams CreateWorkerJob(StratumClient client) { var context = client.ContextAs <CryptonoteWorkerContext>(); var job = new CryptonoteWorkerJob(NextJobId(), context.Difficulty); manager.PrepareWorkerJob(job, out var blob, out var target); // should never happen if (string.IsNullOrEmpty(blob) || string.IsNullOrEmpty(blob)) { return(null); } var result = new CryptonoteJobParams { JobId = job.Id, Blob = blob, Target = target }; // update context lock (context) { context.AddJob(job); } return(result); }
protected override void OnSubscribe(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { var request = tsRequest.Value; var context = client.ContextAs <BitcoinWorkerContext>(); if (request.Id == null) { client.RespondError(StratumError.Other, "missing request id", request.Id); return; } var requestParams = request.ParamsAs <string[]>(); var data = new object[] { client.ConnectionId, } .Concat(manager.GetSubscriberData(client)) .ToArray(); client.Respond(data, request.Id); // setup worker context context.IsSubscribed = true; context.UserAgent = requestParams?.Length > 0 ? requestParams[0].Trim() : null; }
private void EnsureInitialWorkSent(StratumClient client) { var context = client.ContextAs <AionWorkerContext>(); ArrayList arrayTarget = new ArrayList(); var sendInitialWork = false; lock (context) { if (context.IsSubscribed && context.IsAuthorized && !context.IsInitialWorkSent) { context.IsInitialWorkSent = true; string newTarget = AionUtils.diffToTarget(context.Difficulty); arrayTarget.Add(newTarget); sendInitialWork = true; } } if (sendInitialWork) { // send intial update // await client.NotifyAsync(AionStratumMethods.MiningNotify, currentJobParams); // await client.NotifyAsync(AionStratumMethods.SetTarget, arrayTarget); client.Notify(AionStratumMethods.MiningNotify, currentJobParams); client.Notify(AionStratumMethods.SetTarget, arrayTarget); } }
protected virtual async Task OnSubscribeAsync(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { var request = tsRequest.Value; if (request.Id == null) { throw new StratumException(StratumError.MinusOne, "missing request id"); } var context = client.ContextAs <BitcoinWorkerContext>(); var requestParams = request.ParamsAs <string[]>(); var data = new object[] { new object[] { new object[] { BitcoinStratumMethods.SetDifficulty, client.ConnectionId }, new object[] { BitcoinStratumMethods.MiningNotify, client.ConnectionId } } } .Concat(manager.GetSubscriberData(client)) .ToArray(); await client.RespondAsync(data, request.Id); // setup worker context context.IsSubscribed = true; context.UserAgent = requestParams?.Length > 0 ? requestParams[0].Trim() : null; // send intial update await client.NotifyAsync(BitcoinStratumMethods.SetDifficulty, new object[] { context.Difficulty }); await client.NotifyAsync(BitcoinStratumMethods.MiningNotify, currentJobParams); }
protected async Task OnSubscribeAsync(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { var request = tsRequest.Value; var context = client.ContextAs <BitcoinWorkerContext>(); if (request.Id == null) { throw new StratumException(StratumError.MinusOne, "missing request id"); } var requestParams = request.ParamsAs <string[]>(); var data = new object[] { client.ConnectionId, } .Concat(manager.GetSubscriberData(client)) .ToArray(); await client.RespondAsync(data, request.Id); // setup worker context context.IsSubscribed = true; context.UserAgent = requestParams?.Length > 0 ? requestParams[0].Trim() : null; }
protected virtual async Task OnAuthorizeAsync(StratumClient client, Timestamped<JsonRpcRequest> tsRequest, CancellationToken ct) { var request = tsRequest.Value; if (request.Id == null) throw new StratumException(StratumError.MinusOne, "missing request id"); var context = client.ContextAs<BitcoinWorkerContext>(); var requestParams = request.ParamsAs<string[]>(); var workerValue = requestParams?.Length > 0 ? requestParams[0] : null; var password = requestParams?.Length > 1 ? requestParams[1] : null; var passParts = password?.Split(PasswordControlVarsSeparator); // extract worker/miner var split = workerValue?.Split('.'); var minerName = split?.FirstOrDefault()?.Trim(); var workerName = split?.Skip(1).FirstOrDefault()?.Trim() ?? string.Empty; // assumes that workerName is an address context.IsAuthorized = !string.IsNullOrEmpty(minerName) && await manager.ValidateAddressAsync(minerName, ct); context.Miner = minerName; context.Worker = workerName; if (context.IsAuthorized) { // respond await client.RespondAsync(context.IsAuthorized, request.Id); // log association logger.Info(() => $"[{client.ConnectionId}] Authorized worker {workerValue}"); // extract control vars from password var staticDiff = GetStaticDiffFromPassparts(passParts); if (staticDiff.HasValue && (context.VarDiff != null && staticDiff.Value >= context.VarDiff.Config.MinDiff || context.VarDiff == null && staticDiff.Value > context.Difficulty)) { context.VarDiff = null; // disable vardiff context.SetDifficulty(staticDiff.Value); logger.Info(() => $"[{client.ConnectionId}] Setting static difficulty of {staticDiff.Value}"); await client.NotifyAsync(BitcoinStratumMethods.SetDifficulty, new object[] { context.Difficulty }); } } else { // respond await client.RespondErrorAsync(StratumError.UnauthorizedWorker, "Authorization failed", request.Id, context.IsAuthorized); // issue short-time ban if unauthorized to prevent DDos on daemon (validateaddress RPC) logger.Info(() => $"[{client.ConnectionId}] Banning unauthorized worker for 60 sec"); banManager.Ban(client.RemoteEndpoint.Address, TimeSpan.FromSeconds(60)); DisconnectClient(client); } }
protected virtual Task OnVarDiffUpdateAsync(StratumClient client, double newDiff) { var context = client.ContextAs <WorkerContextBase>(); context.EnqueueNewDifficulty(newDiff); return(Task.FromResult(true)); }
public async ValueTask <Share> SubmitShareAsync(StratumClient worker, CryptonoteSubmitShareRequest request, CryptonoteWorkerJob workerJob, double stratumDifficultyBase, CancellationToken ct) { Contract.RequiresNonNull(worker, nameof(worker)); Contract.RequiresNonNull(request, nameof(request)); logger.Debug(() => $"{ worker.ConnectionId } {request}"); var context = worker.ContextAs <CryptonoteWorkerContext>(); var job = currentJob; if (workerJob.Height != job?.BlockTemplate.Height) { throw new StratumException(StratumError.MinusOne, "block expired"); } // validate & process share var(share, blobHex) = job.ProcessShare(request.Nonce, workerJob.ExtraNonce, request.Hash, worker); // enrich share with common data share.PoolId = poolConfig.Id; share.IpAddress = worker.RemoteEndpoint.Address.ToString(); share.Miner = context.Miner; share.Worker = context.Worker; share.UserAgent = context.UserAgent; share.Source = clusterConfig.ClusterName; share.NetworkDifficulty = job.BlockTemplate.Difficulty; share.Created = clock.UtcNow; // if block candidate, submit & check if accepted by network if (share.IsBlockCandidate) { logger.Info(() => $"Submitting block {share.BlockHeight} [{share.BlockHash.Substring(0, 6)}]"); share.IsBlockCandidate = await SubmitBlockAsync(share, blobHex, share.BlockHash); if (share.IsBlockCandidate) { logger.Info(() => $"Daemon accepted block {share.BlockHeight} [{share.BlockHash.Substring(0, 6)}] submitted by {context.Miner}"); OnBlockFound(); share.TransactionConfirmationData = share.BlockHash; } else { // clear fields that no longer apply share.TransactionConfirmationData = null; } } return(share); }
public async Task <Share> SubmitShareAsync(StratumClient worker, MoneroSubmitShareRequest request, MoneroWorkerJob workerJob, double stratumDifficultyBase) { Contract.RequiresNonNull(worker, nameof(worker)); Contract.RequiresNonNull(request, nameof(request)); logger.LogInvoke(new[] { worker.ConnectionId }); var context = worker.ContextAs <MoneroWorkerContext>(); var job = currentJob; if (workerJob.Height != job?.BlockTemplate.Height) { throw new StratumException(StratumError.MinusOne, "block expired"); } // validate & process var(share, blobHex, blobHash) = job.ProcessShare(request.Nonce, workerJob.ExtraNonce, request.Hash, worker); // enrich share with common data share.PoolId = poolConfig.Id; share.IpAddress = worker.RemoteEndpoint.Address.ToString(); share.Miner = context.MinerName; share.Worker = context.WorkerName; share.PayoutInfo = context.PaymentId; share.UserAgent = context.UserAgent; share.Source = clusterConfig.ClusterName; share.NetworkDifficulty = job.BlockTemplate.Difficulty; share.Created = clock.Now; // if block candidate, submit & check if accepted by network if (share.IsBlockCandidate) { logger.Info(() => $"Submitting block {share.BlockHeight} [{blobHash.Substring(0, 6)}]"); share.IsBlockCandidate = await SubmitBlockAsync(share, blobHex, blobHash); if (share.IsBlockCandidate) { logger.Info(() => $"Daemon accepted block {share.BlockHeight} [{blobHash.Substring(0, 6)}] submitted by {context.MinerName}"); blockSubmissionSubject.OnNext(Unit.Default); share.TransactionConfirmationData = blobHash; } else { // clear fields that no longer apply share.TransactionConfirmationData = null; } } return(share); }
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)); }
private async Task OnSubmitHashrateAsync(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { var request = tsRequest.Value; var context = client.ContextAs <EthereumWorkerContext>(); if (request.Id == null) { throw new StratumException(StratumError.Other, "missing request id"); } // Dummy command, just predend like you did something with it and send true to keep the miner happy await client.RespondAsync(true, request.Id); }
protected override async Task OnAuthorizeAsync(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { await base.OnAuthorizeAsync(client, tsRequest); var context = client.ContextAs <BitcoinWorkerContext>(); if (context.IsAuthorized) { // send intial update client.Notify(ZCashStratumMethods.SetTarget, new object[] { EncodeTarget(context.Difficulty) }); client.Notify(BitcoinStratumMethods.MiningNotify, currentJobParams); } }
protected override async Task OnVarDiffUpdateAsync(StratumClient client, double newDiff) { var context = client.ContextAs<BitcoinWorkerContext>(); context.EnqueueNewDifficulty(newDiff); // apply immediately and notify client if (context.HasPendingDifficulty) { context.ApplyPendingDifficulty(); await client.NotifyAsync(BitcoinStratumMethods.SetDifficulty, new object[] { context.Difficulty }); await client.NotifyAsync(BitcoinStratumMethods.MiningNotify, currentJobParams); } }
private async Task OnSubmitLoginAsync(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { var request = tsRequest.Value; var context = client.ContextAs <EthereumWorkerContext>(); if (request.Id == null) { throw new StratumException(StratumError.MinusOne, "missing request id"); } context.IsSubscribed = true; var requestParams = request.ParamsAs <string[]>(); // setup worker context var workerValue = requestParams?.Length > 0 ? requestParams[0] : "0"; var password = requestParams?.Length > 1 ? requestParams[1] : null; var passParts = password?.Split(PasswordControlVarsSeparator); // extract worker/miner var workerParts = workerValue?.Split('.'); var minerName = workerParts?.Length > 0 ? workerParts[0].Trim() : null; var workerName = workerParts?.Length > 1 ? workerParts[1].Trim() : "0"; // assumes that workerName is an address context.IsAuthorized = !string.IsNullOrEmpty(minerName) && manager.ValidateAddress(minerName); context.Miner = minerName.ToLower(); context.Worker = workerName; context.IsNiceHashClient = false; // respond await client.RespondAsync(context.IsAuthorized, request.Id); // extract control vars from password var staticDiff = GetStaticDiffFromPassparts(passParts); if (staticDiff.HasValue && (context.VarDiff != null && staticDiff.Value >= context.VarDiff.Config.MinDiff || context.VarDiff == null && staticDiff.Value > context.Difficulty)) { context.VarDiff = null; // disable vardiff context.SetDifficulty(staticDiff.Value); logger.Info(() => $"[{client.ConnectionId}] Setting static difficulty of {staticDiff.Value}"); } await EnsureInitialWorkSent(client); // log association logger.Info(() => $"[{client.ConnectionId}] Authorized Stratum-Proxy Worker {workerValue}"); }
public virtual (Share Share, string BlockHex) ProcessShare(StratumClient worker, string extraNonce, string nTime, string nonce, string solution) { Contract.RequiresNonNull(worker, nameof(worker)); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(extraNonce), $"{nameof(extraNonce)} must not be empty"); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(nTime), $"{nameof(nTime)} must not be empty"); Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(solution), $"{nameof(solution)} 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 < BlockHeader.Timestamp || nTimeInt > ((DateTimeOffset)clock.Now).ToUnixTimeSeconds() + 7200) { throw new StratumException(StratumError.Other, "ntime out of range"); } // validate nonce if (extraNonce.Length != 24) { throw new StratumException(StratumError.Other, "incorrect size of extraNonce2"); } var extraNonce2 = extraNonce.Substring(0, extraNonce.Length - 8); if (!extraNonce.Equals(extraNonce2 + context.ExtraNonce1)) { throw new StratumException(StratumError.Other, "incorrect extraNonce"); } // validate solution if (solution.Length != chainConfig.SolutionSize * 2) { throw new StratumException(StratumError.Other, "incorrect size of solution"); } // dupe check if (!RegisterSubmit(extraNonce, nonce, solution)) { throw new StratumException(StratumError.DuplicateShare, "duplicate share"); } return(ProcessShareInternal(worker, extraNonce, nTimeInt, nonce, solution)); }
public async Task <Share> SubmitShareAsync(StratumClient worker, string[] request, double stratumDifficulty, double stratumDifficultyBase) { Contract.RequiresNonNull(worker, nameof(worker)); Contract.RequiresNonNull(request, nameof(request)); logger.LogInvoke(new[] { worker.ConnectionId }); var context = worker.ContextAs <AionWorkerContext>(); var miner = request[0]; var jobId = request[1]; var time = request[2]; var nonce = request[3]; var soln = request[4]; AionJob job; // stale? lock (jobLock) { if (!validJobs.TryGetValue(jobId, out job)) { // logger.Info(() => $"!!! src/Mingingcore/Blockchain/Aion/AionJobManager.cs/SubmitShareAsync stale-share jobId '{jobId}'"); throw new StratumException(StratumError.MinusOne, "stale share"); } } // validate & process var(share, fullNonceHex, solution, headerHash, nTime) = await job.ProcessShareAsync(worker, nonce, time, soln); // enrich share with common data share.PoolId = poolConfig.Id; share.Source = clusterConfig.ClusterName; share.Created = clock.Now; // if block candidate, submit & check if accepted by network if (share.IsBlockCandidate) { logger.Info(() => $"Submitting block {share.BlockHeight}"); share.IsBlockCandidate = await SubmitBlockAsync(share, fullNonceHex, headerHash, solution, nTime); if (share.IsBlockCandidate) { logger.Info(() => $"Daemon accepted block {share.BlockHeight} submitted by {context.MinerName}"); } } return(share); }
private async Task OnAuthorizeAsync(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { var request = tsRequest.Value; var context = client.ContextAs <AionWorkerContext>(); if (request.Id == null) { throw new StratumException(StratumError.MinusOne, "missing request id"); } var requestParams = request.ParamsAs <string[]>(); var workerValue = requestParams?.Length > 0 ? requestParams[0] : null; var password = requestParams?.Length > 1 ? requestParams[1] : null; var passParts = password?.Split(PasswordControlVarsSeparator); // extract worker/miner var workerParts = workerValue?.Split('.'); var minerName = workerParts?.Length > 0 ? workerParts[0].Trim() : null; var workerName = workerParts?.Length > 1 ? workerParts[1].Trim() : null; var minimumPayment = GetMinimumPaymentFromPassparts(passParts); // assumes that workerName is an address context.IsAuthorized = !string.IsNullOrEmpty(minerName) && await manager.ValidateAddressAsync(minerName); context.MinerName = minerName; context.WorkerName = workerName; // respond // await client.RespondAsync(context.IsAuthorized, request.Id); client.Respond(context.IsAuthorized, request.Id); // extract control vars from password var staticDiff = GetStaticDiffFromPassparts(passParts); if (staticDiff.HasValue && (context.VarDiff != null && staticDiff.Value >= context.VarDiff.Config.MinDiff || context.VarDiff == null && staticDiff.Value > context.Difficulty)) { context.VarDiff = null; // disable vardiff context.SetDifficulty(staticDiff.Value); } messageBus.SendMessage(new MinerInfo(poolConfig.Id, context.MinerName, context.MinimumPayment = minimumPayment)); EnsureInitialWorkSent(client); // log association logger.Info(() => $"[{client.ConnectionId}] Authorized worker {workerValue} mp {minimumPayment}"); }
protected override void OnVarDiffUpdate(StratumClient client, double newDiff) { var context = client.ContextAs <BitcoinWorkerContext>(); context.EnqueueNewDifficulty(newDiff); // apply immediately and notify client if (context.HasPendingDifficulty) { context.ApplyPendingDifficulty(); client.Notify(ZCashStratumMethods.SetTarget, new object[] { EncodeTarget(context.Difficulty) }); client.Notify(BitcoinStratumMethods.MiningNotify, currentJobParams); } }
private void EnsureInitialWorkSent(StratumClient client) { var context = client.ContextAs <EthereumWorkerContext>(); lock (context) { if (context.IsAuthorized && context.IsAuthorized && !context.IsInitialWorkSent) { context.IsInitialWorkSent = true; // send intial update client.Notify(EthereumStratumMethods.SetDifficulty, new object[] { context.Difficulty }); client.Notify(EthereumStratumMethods.MiningNotify, currentJobParams); } } }
protected override async Task OnVarDiffUpdateAsync(StratumClient client, double newDiff) { await base.OnVarDiffUpdateAsync(client, newDiff); // apply immediately and notify client var context = client.ContextAs <CryptonoteWorkerContext>(); if (context.HasPendingDifficulty) { context.ApplyPendingDifficulty(); // re-send job var job = CreateWorkerJob(client); await client.NotifyAsync(CryptonoteStratumMethods.JobNotify, job); } }
protected override void OnVarDiffUpdate(StratumClient client, double newDiff) { base.OnVarDiffUpdate(client, newDiff); // apply immediately and notify client var context = client.ContextAs <MoneroWorkerContext>(); if (context.HasPendingDifficulty) { context.ApplyPendingDifficulty(); // re-send job var job = CreateWorkerJob(client); client.Notify(MoneroStratumMethods.JobNotify, job); } }
protected override void OnVarDiffUpdate(StratumClient client, double newDiff) { base.OnVarDiffUpdate(client, newDiff); // apply immediately and notify client var context = client.ContextAs <EthereumWorkerContext>(); if (context.HasPendingDifficulty) { context.ApplyPendingDifficulty(); // send job client.Notify(EthereumStratumMethods.SetDifficulty, new object[] { context.Difficulty }); client.Notify(EthereumStratumMethods.MiningNotify, currentJobParams); } }
public async Task <Share> SubmitShareAsync(StratumClient worker, string[] request, double stratumDifficulty, double stratumDifficultyBase) { Contract.RequiresNonNull(worker, nameof(worker)); Contract.RequiresNonNull(request, nameof(request)); logger.LogInvoke(LogCat, new[] { worker.ConnectionId }); var context = worker.ContextAs <EthereumWorkerContext>(); // var miner = request[0]; var jobId = request[1]; var nonce = request[2]; EthereumJob job; // stale? lock (jobLock) { if (!validJobs.TryGetValue(jobId, out job)) { throw new StratumException(StratumError.MinusOne, "stale share"); } } // validate & process var(share, fullNonceHex, headerHash, mixHash) = await job.ProcessShareAsync(worker, nonce, ethash); // enrich share with common data share.PoolId = poolConfig.Id; share.NetworkDifficulty = BlockchainStats.NetworkDifficulty; share.Source = clusterConfig.ClusterName; share.Created = clock.Now; // if block candidate, submit & check if accepted by network if (share.IsBlockCandidate) { logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight}"); share.IsBlockCandidate = await SubmitBlockAsync(share, fullNonceHex, headerHash, mixHash); if (share.IsBlockCandidate) { logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} submitted by {context.MinerName}"); } } return(share); }
private async Task OnSuggestTargetAsync(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { var request = tsRequest.Value; var context = client.ContextAs <BitcoinWorkerContext>(); if (request.Id == null) { await client.RespondErrorAsync(StratumError.Other, "missing request id", request.Id); return; } var requestParams = request.ParamsAs <string[]>(); var target = requestParams.FirstOrDefault(); if (!string.IsNullOrEmpty(target)) { if (System.Numerics.BigInteger.TryParse(target, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var targetBig)) { var newDiff = (double)new BigRational(chainConfig.Diff1b, targetBig); var poolEndpoint = poolConfig.Ports[client.PoolEndpoint.Port]; if (newDiff >= poolEndpoint.Difficulty) { context.EnqueueNewDifficulty(newDiff); context.ApplyPendingDifficulty(); await client.NotifyAsync(ZCashStratumMethods.SetTarget, new object[] { EncodeTarget(context.Difficulty) }); } else { await client.RespondErrorAsync(StratumError.Other, "suggested difficulty too low", request.Id); } } else { await client.RespondErrorAsync(StratumError.Other, "invalid target", request.Id); } } else { await client.RespondErrorAsync(StratumError.Other, "invalid target", request.Id); } }
public override object[] GetSubscriberData(StratumClient worker) { Contract.RequiresNonNull(worker, nameof(worker)); var context = worker.ContextAs <BitcoinWorkerContext>(); // assign unique ExtraNonce1 to worker (miner) context.ExtraNonce1 = extraNonceProvider.Next(); // setup response data var responseData = new object[] { context.ExtraNonce1 }; return(responseData); }
private async Task OnAuthorizeAsync(StratumClient client, Timestamped <JsonRpcRequest> tsRequest) { var request = tsRequest.Value; var context = client.ContextAs <EthereumWorkerContext>(); if (request.Id == null) { await client.RespondErrorAsync(StratumError.Other, "missing request id", request.Id); return; } var requestParams = request.ParamsAs <string[]>(); var workerValue = requestParams?.Length > 0 ? requestParams[0] : null; var password = requestParams?.Length > 1 ? requestParams[1] : null; var passParts = password?.Split(PasswordControlVarsSeparator); // extract worker/miner var workerParts = workerValue?.Split('.'); var minerName = workerParts?.Length > 0 ? workerParts[0].Trim() : null; var workerName = workerParts?.Length > 1 ? workerParts[1].Trim() : null; // assumes that workerName is an address context.IsAuthorized = !string.IsNullOrEmpty(minerName) && manager.ValidateAddress(minerName); context.MinerName = minerName; context.WorkerName = workerName; // respond await client.RespondAsync(context.IsAuthorized, request.Id); // extract control vars from password var staticDiff = GetStaticDiffFromPassparts(passParts); if (staticDiff.HasValue && (context.VarDiff != null && staticDiff.Value >= context.VarDiff.Config.MinDiff || context.VarDiff == null && staticDiff.Value > context.Difficulty)) { context.VarDiff = null; // disable vardiff context.SetDifficulty(staticDiff.Value); } await EnsureInitialWorkSent(client); // log association logger.Info(() => $"[{LogCat}] [{client.ConnectionId}] = {workerValue} = {client.RemoteEndpoint.Address}"); }