private void OnShare(object sender, EventArgs e) { var shareArgs = (ShareEventArgs)e; var miner = shareArgs.Miner; if (miner == null) { return; } int now = TimeHelpers.NowInUnixTime(); if (miner.VardiffBuffer == null) { miner.LastVardiffRetarget = now - (Config.RetargetTime / 2); miner.LastVardiffTimestamp = now; miner.VardiffBuffer = new RingBuffer(_bufferSize); return; } var sinceLast = now - miner.LastVardiffTimestamp; // how many seconds elapsed since last share? miner.VardiffBuffer.Append(sinceLast); // append it to vardiff buffer. miner.LastVardiffTimestamp = now; if (now - miner.LastVardiffRetarget < Config.RetargetTime && miner.VardiffBuffer.Size > 0) // check if we need a re-target. { return; } miner.LastVardiffRetarget = now; var average = miner.VardiffBuffer.Average; var deltaDiff = Config.TargetTime / average; if (average > _tMax && miner.Difficulty > Config.MinimumDifficulty) { if (deltaDiff * miner.Difficulty < Config.MinimumDifficulty) { deltaDiff = Config.MinimumDifficulty / miner.Difficulty; } } else if (average < _tMin) { if (deltaDiff * miner.Difficulty > Config.MaximumDifficulty) { deltaDiff = Config.MaximumDifficulty / miner.Difficulty; } } else { return; } var newDifficulty = miner.Difficulty * deltaDiff; // calculate the new difficulty. miner.SetDifficulty(newDifficulty); // set the new difficulty and send it. _logger.Debug("Difficulty updated to {0} for miner: {1:l}", miner.Difficulty, miner.Username); miner.VardiffBuffer.Clear(); }
public void AddShare(IShare share) { try { if (!IsEnabled || !_redisProvider.IsConnected) { return; } //_client.StartPipe(); // batch the commands. // add the share to round var currentKey = string.Format("{0}:shares:round:current", _coin); _redisProvider.Client.HIncrByFloat(currentKey, share.Miner.Username, share.Difficulty); // increment shares stats. var statsKey = string.Format("{0}:stats", _coin); _redisProvider.Client.HIncrBy(statsKey, share.IsValid ? "validShares" : "invalidShares", 1); // add to hashrate if (share.IsValid) { var hashrateKey = string.Format("{0}:hashrate", _coin); var entry = string.Format("{0}:{1}", share.Difficulty, share.Miner.Username); _redisProvider.Client.ZAdd(hashrateKey, Tuple.Create(TimeHelpers.NowInUnixTime(), entry)); } //_client.EndPipe(); // execute the batch commands. } catch (Exception e) { _logger.Error("An exception occured while comitting share: {0:l}", e.Message); } }
private void ReadHashrate() { // read hashrate stats. var windowTime = TimeHelpers.NowInUnixTime() - _statisticsConfig.HashrateWindow; _storage.DeleteExpiredHashrateData(windowTime); var hashrates = _storage.GetHashrateData(windowTime); double total = hashrates.Sum(pair => pair.Value); Hashrate = Convert.ToUInt64(_shareMultiplier * total / _statisticsConfig.HashrateWindow); }
/// <summary> /// Creates a new instance of generation transaction. /// </summary> /// <param name="extraNonce">The extra nonce.</param> /// <param name="daemonClient">The daemon client.</param> /// <param name="blockTemplate">The block template.</param> /// <param name="poolConfig">The associated pool's configuration</param> /// <remarks> /// Reference implementations: /// https://github.com/zone117x/node-stratum-pool/blob/b24151729d77e0439e092fe3a1cdbba71ca5d12e/lib/transactions.js /// https://github.com/Crypto-Expert/stratum-mining/blob/master/lib/coinbasetx.py /// </remarks> public GenerationTransaction(IExtraNonce extraNonce, IDaemonClient daemonClient, IBlockTemplate blockTemplate, IPoolConfig poolConfig) { // TODO: we need a whole refactoring here. // we should use DI and it shouldn't really require daemonClient connection to function. BlockTemplate = blockTemplate; ExtraNonce = extraNonce; PoolConfig = poolConfig; Version = (UInt32)(poolConfig.Coin.SupportsTxMessages ? 2 : 1); TxMessage = Serializers.SerializeString(poolConfig.Meta.TxMessage); LockTime = 0; // transaction inputs Inputs = new List <TxIn> { new TxIn { PreviousOutput = new OutPoint { Hash = Hash.ZeroHash, Index = (UInt32)Math.Pow(2, 32) - 1 }, Sequence = 0x0, SignatureScript = new SignatureScript( blockTemplate.Height, blockTemplate.CoinBaseAux.Flags, TimeHelpers.NowInUnixTime(), (byte)extraNonce.ExtraNoncePlaceholder.Length, "/CoiniumServ/") } }; // transaction outputs Outputs = new Outputs(daemonClient); double blockReward = BlockTemplate.Coinbasevalue; // the amount rewarded by the block. // generate output transactions for recipients (set in config). foreach (var pair in poolConfig.Rewards) { var amount = blockReward * pair.Value / 100; // calculate the amount he recieves based on the percent of his shares. blockReward -= amount; Outputs.AddRecipient(pair.Key, amount); } // send the remaining coins to pool's central wallet. Outputs.AddPoolWallet(poolConfig.Wallet.Adress, blockReward); }
private bool BanExpired(IPAddress ip) { var banTime = _bannedIps[ip]; var elapsedTime = TimeHelpers.NowInUnixTime() - banTime; // elapsed time since his ban. var timeLeft = Config.Duration - elapsedTime; // time left for his ban if (timeLeft > 0) // if he has still remaining time { return(false); } return(true); }
private void Ban(IMiner miner) { // TODO: add vanilla miners to banlist too. if (miner is IVanillaMiner) // as vanilla miners doesn't use persistent connections, we don't need to disconect him { return; // but just blacklist his ip. } var client = (IClient)miner; var ip = client.Connection.RemoteEndPoint.Address; if (!_bannedIps.ContainsKey(ip)) { _bannedIps.Add(ip, TimeHelpers.NowInUnixTime()); } client.Connection.Disconnect(); }
private void CleanUp(object state) { var startingCount = _jobs.Count; // calculate the cleanup delta time - jobs created before this will be cleaned up. var delta = TimeHelpers.NowInUnixTime() - _cleanupFrequency; // find expired jobs that were created before our calcualted delta time. _jobs = _jobs.Where(j => j.Value.CreationTime >= delta || j.Value == Current) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); var cleanedCount = startingCount - _jobs.Count; if (cleanedCount > 0) { _logger.Debug("Cleaned-up {0} expired jobs", cleanedCount); } _cleanupTimer.Change(_cleanupFrequency * 1000, Timeout.Infinite); // reset the cleanup timer. }
/// <summary> /// Creates a new instance of JobNotification. /// </summary> /// <param name="id"></param> /// <param name="algorithm"></param> /// <param name="blockTemplate"></param> /// <param name="generationTransaction"></param> public Job(UInt64 id, IHashAlgorithm algorithm, IBlockTemplate blockTemplate, IGenerationTransaction generationTransaction) { // init the values. Id = id; HashAlgorithm = algorithm; BlockTemplate = blockTemplate; Height = blockTemplate.Height; GenerationTransaction = generationTransaction; PreviousBlockHash = blockTemplate.PreviousBlockHash.HexToByteArray().ToHexString(); PreviousBlockHashReversed = blockTemplate.PreviousBlockHash.HexToByteArray().ReverseByteOrder().ToHexString(); CoinbaseInitial = generationTransaction.Initial.ToHexString(); CoinbaseFinal = generationTransaction.Final.ToHexString(); CreationTime = TimeHelpers.NowInUnixTime(); _shares = new List <UInt64>(); // calculate the merkle tree MerkleTree = new MerkleTree(BlockTemplate.Transactions.GetHashList()); // set version Version = BitConverter.GetBytes(blockTemplate.Version.BigEndian()).ToHexString(); // set the encoded difficulty (bits) EncodedDifficulty = blockTemplate.Bits; // set the target Target = string.IsNullOrEmpty(blockTemplate.Target) ? EncodedDifficulty.BigIntFromBitsHex() : BigInteger.Parse(blockTemplate.Target, NumberStyles.HexNumber); // set the block diff Difficulty = ((double)new BigRational(AlgorithmManager.Diff1, Target)); // set the ntime NTime = BitConverter.GetBytes(blockTemplate.CurTime.BigEndian()).ToHexString(); }
public Share(IStratumMiner miner, UInt64 jobId, IJob job, string extraNonce2, string nTimeString, string nonceString) { Miner = miner; JobId = jobId; Job = job; Error = ShareError.None; var submitTime = TimeHelpers.NowInUnixTime(); // time we recieved the share from miner. if (Job == null) { Error = ShareError.JobNotFound; return; } // check size of miner supplied extraNonce2 if (extraNonce2.Length / 2 != ExtraNonce.ExpectedExtraNonce2Size) { Error = ShareError.IncorrectExtraNonce2Size; return; } ExtraNonce2 = Convert.ToUInt32(extraNonce2, 16); // set extraNonce2 for the share. // check size of miner supplied nTime. if (nTimeString.Length != 8) { Error = ShareError.IncorrectNTimeSize; return; } NTime = Convert.ToUInt32(nTimeString, 16); // read ntime for the share // make sure NTime is within range. if (NTime < job.BlockTemplate.CurTime || NTime > submitTime + 7200) { Error = ShareError.NTimeOutOfRange; return; } // check size of miner supplied nonce. if (nonceString.Length != 8) { Error = ShareError.IncorrectNonceSize; return; } Nonce = Convert.ToUInt32(nonceString, 16); // nonce supplied by the miner for the share. // set job supplied parameters. Height = job.BlockTemplate.Height; // associated job's block height. ExtraNonce1 = miner.ExtraNonce; // extra nonce1 assigned to miner. // check for duplicate shares. if (!Job.RegisterShare(this)) // try to register share with the job and see if it's duplicated or not. { Error = ShareError.DuplicateShare; return; } // construct the coinbase. CoinbaseBuffer = Serializers.SerializeCoinbase(Job, ExtraNonce1, ExtraNonce2); CoinbaseHash = Coin.Coinbase.Utils.HashCoinbase(CoinbaseBuffer); // create the merkle root. MerkleRoot = Job.MerkleTree.WithFirst(CoinbaseHash).ReverseBuffer(); // create the block headers HeaderBuffer = Serializers.SerializeHeader(Job, MerkleRoot, NTime, Nonce); HeaderHash = Job.HashAlgorithm.Hash(HeaderBuffer, miner.Pool.Config.Coin.Options); HeaderValue = new BigInteger(HeaderHash); // calculate the share difficulty Difficulty = ((double)new BigRational(AlgorithmManager.Diff1, HeaderValue)) * Job.HashAlgorithm.Multiplier; // calculate the block difficulty BlockDiffAdjusted = Job.Difficulty * Job.HashAlgorithm.Multiplier; // check if block candicate if (Job.Target >= HeaderValue) { IsBlockCandidate = true; BlockHex = Serializers.SerializeBlock(Job, HeaderBuffer, CoinbaseBuffer, miner.Pool.Config.Coin.IsPOS); BlockHash = HeaderBuffer.DoubleDigest().ReverseBuffer(); } else { IsBlockCandidate = false; BlockHash = HeaderBuffer.DoubleDigest().ReverseBuffer(); // Check if share difficulty reaches miner difficulty. var lowDifficulty = Difficulty / miner.Difficulty < 0.99; // share difficulty should be equal or more then miner's target difficulty. if (!lowDifficulty) // if share difficulty is high enough to match miner's current difficulty. { return; // just accept the share. } if (Difficulty >= miner.PreviousDifficulty) // if the difficulty matches miner's previous difficulty before the last vardiff triggered difficulty change { return; // still accept the share. } // if the share difficulty can't match miner's current difficulty or previous difficulty Error = ShareError.LowDifficultyShare; // then just reject the share with low difficult share error. } }