private async Task <(Share Share, string nonce, string solution, string headerHash, string nTime)> ProcessShareInternal( StratumClient worker, string nonce, string nTime, string solution) { var context = worker.GetContextAs <AionWorkerContext>(); var solutionBytes = solution.HexToByteArray(); // serialize block-header var headerBytes = SerializeHeader(nonce); // verify solution if (!equihash.Verify210(headerBytes, solutionBytes)) { throw new StratumException(StratumError.Other, "invalid solution"); } // hash block-header var headerSolutionBytes = headerBytes.Concat(solutionBytes).ToArray(); var headerHash = headerHasher.Digest(headerSolutionBytes); var headerHashReversed = headerHash.ToReverseArray(); var headerBigInt = headerHashReversed.ToBigInteger(); var target = new BigInteger(blockTarget.ToBytes()); var isBlockCandidate = target > headerBigInt; // calc share-diff var stratumDifficulty = context.Difficulty > Difficulty ? Difficulty : context.Difficulty; var shareDiff = stratumDifficulty; var ratio = shareDiff / stratumDifficulty; var sentTargetInt = new uint256(AionUtils.diffToTarget(context.Difficulty).HexToByteArray().ReverseArray()); var sentTarget = new BigInteger(sentTargetInt.ToBytes()); var isLowDiffShare = sentTarget <= headerBigInt; if (isLowDiffShare) { throw new StratumException(StratumError.LowDifficultyShare, $"low difficulty share ({shareDiff})"); } var result = new Share { BlockHeight = (long)BlockTemplate.Height, IpAddress = worker.RemoteEndpoint?.Address?.ToString(), Miner = context.MinerName, Worker = context.WorkerName, UserAgent = context.UserAgent, NetworkDifficulty = Difficulty, Difficulty = stratumDifficulty, IsBlockCandidate = isBlockCandidate, TransactionConfirmationData = headerHash.ToHexString(), }; if (isBlockCandidate) { result.BlockReward = AionUtils.calculateReward((long)BlockTemplate.Height); result.BlockHash = headerHashReversed.ToHexString(); } return(result, nonce, solution, BlockTemplate.HeaderHash, nTime); }
private void EnsureInitialWorkSent(StratumClient client) { var context = client.GetContextAs <AionWorkerContext>(); lock (context) { if (context.IsAuthorized && context.IsAuthorized && !context.IsInitialWorkSent) { context.IsInitialWorkSent = true; string newTarget = AionUtils.diffToTarget(context.Difficulty); // update the difficulty (((object[])currentJobParams)[2]) = newTarget; // send intial update client.Notify(AionStratumMethods.MiningNotify, currentJobParams); } } }
private void EnsureInitialWorkSent(StratumClient client) { var context = client.GetContextAs <AionWorkerContext>(); lock (context) { if (context.IsAuthorized && !context.IsInitialWorkSent) { context.IsInitialWorkSent = true; string newTarget = AionUtils.diffToTarget(context.Difficulty); ArrayList arrayTarget = new ArrayList(); arrayTarget.Add(newTarget); // send intial update client.Notify(AionStratumMethods.MiningNotify, currentJobParams); client.Notify(AionStratumMethods.SetTarget, arrayTarget); } } }
protected override void OnVarDiffUpdate(StratumClient client, double newDiff) { base.OnVarDiffUpdate(client, newDiff); // apply immediately and notify client var context = client.GetContextAs <AionWorkerContext>(); if (context.HasPendingDifficulty) { context.ApplyPendingDifficulty(); string newTarget = AionUtils.diffToTarget(newDiff); ArrayList targetArray = new ArrayList(); targetArray.Add(newTarget); // send job client.Notify(AionStratumMethods.SetDifficulty, new object[] { context.Difficulty }); client.Notify(AionStratumMethods.MiningNotify, currentJobParams); client.Notify(AionStratumMethods.SetTarget, targetArray); } }
private void OnNewJob(object jobParams) { currentJobParams = jobParams; logger.Info(() => $"[{LogCat}] Broadcasting job"); ForEachClient(client => { var context = client.GetContextAs <AionWorkerContext>(); if (context.IsSubscribed && context.IsAuthorized && context.IsInitialWorkSent) { // check alive var lastActivityAgo = clock.Now - context.LastActivity; if (poolConfig.ClientConnectionTimeout > 0 && lastActivityAgo.TotalSeconds > poolConfig.ClientConnectionTimeout) { logger.Info(() => $"[{LogCat}] [{client.ConnectionId}] Booting zombie-worker (idle-timeout exceeded)"); DisconnectClient(client); return; } // varDiff: if the client has a pending difficulty change, apply it now if (context.ApplyPendingDifficulty()) { client.Notify(AionStratumMethods.SetDifficulty, new object[] { context.Difficulty }); } string newTarget = AionUtils.diffToTarget(context.Difficulty); ArrayList arrayTarget = new ArrayList(); arrayTarget.Add(newTarget); client.Notify(AionStratumMethods.MiningNotify, currentJobParams); client.Notify(AionStratumMethods.SetTarget, arrayTarget); } }); }
public async Task <Block[]> ClassifyBlocksAsync(Block[] blocks) { Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(blocks, nameof(blocks)); var pageSize = 100; var pageCount = (int)Math.Ceiling(blocks.Length / (double)pageSize); var blockCache = new Dictionary <long, DaemonResponses.Block>(); var result = new List <Block>(); for (var i = 0; i < pageCount; i++) { // get a page full of blocks var page = blocks .Skip(i * pageSize) .Take(pageSize) .ToArray(); // get latest block var latestBlockResponses = await daemon.ExecuteCmdAllAsync <DaemonResponses.Block>(AionCommands.GetBlockByNumber, new[] { (object)"latest", true }); if (!latestBlockResponses.Any(x => x.Error == null && x.Response?.Height != null)) { break; } var latestBlockHeight = latestBlockResponses.First(x => x.Error == null && x.Response?.Height != null).Response.Height.Value; // execute batch var blockInfos = await FetchBlocks(blockCache, page.Select(block => (long)block.BlockHeight).ToArray()); for (var j = 0; j < blockInfos.Length; j++) { var blockInfo = blockInfos[j]; var block = page[j]; // extract confirmation data from stored block var mixHash = block.TransactionConfirmationData.Split(":").First(); var nonce = block.TransactionConfirmationData.Split(":").Last(); // update progress block.ConfirmationProgress = Math.Min(1.0d, (double)(latestBlockHeight - block.BlockHeight) / extraConfig.MinimumConfirmations); result.Add(block); // is it block mined by us? if (blockInfo.Miner == poolConfig.Address) { // mature? if (latestBlockHeight - block.BlockHeight >= (ulong)extraConfig.MinimumConfirmations) { block.Status = BlockStatus.Confirmed; block.ConfirmationProgress = 1; block.Reward = AionUtils.calculateReward((long)block.BlockHeight); if (extraConfig?.KeepTransactionFees == false && blockInfo.Transactions?.Length > 0) { block.Reward += await GetTxRewardAsync(blockInfo); // tx fees } logger.Info(() => $"[{LogCategory}] Unlocked block {block.BlockHeight} worth {FormatAmount(block.Reward)}"); } continue; } if (block.Status == BlockStatus.Pending && block.ConfirmationProgress > 0.75) { // we've lost this one block.Status = BlockStatus.Orphaned; block.Reward = 0; } } } return(result.ToArray()); }