private void OnShare(object sender, EventArgs e) { var shareArgs = (ShareEventArgs)e; var miner = shareArgs.Miner; if (miner == null) { return; } var now = TimeHelpers.NowInUnixTimestamp(); 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.NowInUnixTimestamp(), entry)); } //_client.EndPipe(); // execute the batch commands. } catch (Exception e) { _logger.Error("An exception occured while comitting share: {0:l}", e.Message); } }
/// <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 = 1;//change version for bitcoin like 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 = (UInt32)Math.Pow(2, 32) - 1, SignatureScript = new SignatureScript( blockTemplate.Height, //blockTemplate.CoinBaseAux.Flags, "", TimeHelpers.NowInUnixTimestamp(), (byte)extraNonce.ExtraNoncePlaceholder.Length, "") } }; // transaction outputs Outputs = new Outputs(daemonClient, poolConfig.Coin); 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); }
/// <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 = blockTemplate.Version; 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.NowInUnixTimestamp(), (byte)extraNonce.ExtraNoncePlaceholder.Length, "/CoiniumServ/") } }; // transaction outputs Outputs = new Outputs(daemonClient, poolConfig.Coin); 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 the 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); // Final output is witness //https://github.com/slush0/stratum-mining/pull/16/files?diff=unified if (!string.IsNullOrEmpty(BlockTemplate.Default_witness_commitment)) { Outputs.AddWitnessOutput(BlockTemplate.Default_witness_commitment.HexToByteArray()); } }
public byte[] Hash(byte[] input) { var now = (UInt64)TimeHelpers.NowInUnixTimestamp(); var index = _timeTable.OrderBy(x => x.Key).First(x => x.Value < now).Key; var nFactor = (int)(Math.Log(index) / Math.Log(2)); var n = 1 << nFactor; return(SCrypt.ComputeDerivedKey(input, input, n, _r, _p, null, 32)); }
private void CalculateHashrate() { // read hashrate stats. var windowTime = TimeHelpers.NowInUnixTimestamp() - _configManager.StatisticsConfig.HashrateWindow; _storage.DeleteExpiredHashrateData(windowTime); var hashrates = _storage.GetHashrateData(windowTime); double total = hashrates.Sum(pair => pair.Value); Hashrate = Convert.ToUInt64(_shareMultiplier * total / _configManager.StatisticsConfig.HashrateWindow); }
private bool BanExpired(IPAddress ip) { var banTime = _bannedIps[ip]; var elapsedTime = TimeHelpers.NowInUnixTimestamp() - 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 CalculateHashrate() { // read hashrate stats. var windowTime = TimeHelpers.NowInUnixTimestamp() - _configManager.StatisticsConfig.HashrateWindow; _storage.DeleteExpiredHashrateData(windowTime); var hashrates = _storage.GetHashrateData(windowTime); double total = hashrates.Sum(pair => pair.Value); Hashrate = _shareMultiplier * total / _configManager.StatisticsConfig.HashrateWindow; // TODO: fix pool hashrate calculation _logger.Debug("Pool hashrate window: {0} total: {1} hashrate: {2:0.000000000}", _configManager.StatisticsConfig.HashrateWindow, total, Hashrate); }
private void Ban(IMiner miner) { // TODO: add vanilla miners to banlist too. if (miner is IGetworkMiner) // 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.NowInUnixTimestamp()); } client.Connection.Disconnect(); }
/// <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.NowInUnixTimestamp(); EdgeBits = blockTemplate.EdgeBits; _shares = new List <UInt64>(); // calculate the merkle tree var hashes = BlockTemplate.Transactions.Slice(1, BlockTemplate.Transactions.Length).GetHashList(); hashes.AddRange(BlockTemplate.Invites.GetHashList()); hashes.AddRange(BlockTemplate.Referrals.GetHashList()); MerkleTree = new MerkleTree(hashes); // 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(); }
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.NowInUnixTimestamp() - _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 generation transaction. /// </summary> /// <param name="extraNonce">The extra nonce.</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, IBlockTemplate blockTemplate, IPoolConfig poolConfig) { _logger = Log.ForContext <GenerationTransaction>(); BlockTemplate = blockTemplate; ExtraNonce = extraNonce; PoolConfig = poolConfig; Version = blockTemplate.Version; TxMessage = Serializers.SerializeString(poolConfig.Meta.TxMessage); LockTime = 0; CoinbaseSignatureScript = new SignatureScript( blockTemplate.Height, blockTemplate.CoinBaseAux.Flags, TimeHelpers.NowInUnixTimestamp(), (byte)extraNonce.ExtraNoncePlaceholder.Length, "/MeritPool/"); }
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); var miner = (IStratumMiner)share.Miner; _redisProvider.Client.HIncrByFloat(currentKey, miner.Username, (double)miner.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 randomModifier = Convert.ToString(miner.ValidShareCount, 16).PadLeft(8, '0'); string modifiedUsername = miner.Username + randomModifier; var entry = string.Format("{0}:{1}", (double)miner.Difficulty, modifiedUsername); _redisProvider.Client.ZAdd(hashrateKey, Tuple.Create((double)TimeHelpers.NowInUnixTimestamp(), entry)); } //_client.EndPipe(); // execute the batch commands. } catch (Exception e) { _logger.Error("An exception occured while comitting share: {0:l}\n{1:l}", e.Message, e.StackTrace); //Bug 2017-07-17 16:00 //An exception occured while comitting share: //"Cannot write to a BufferedStream while the read buffer is not empty if the underlying stream is not seekable. //Ensure that the stream underlying this BufferedStream can seek or avoid interleaving read and write operations on this BufferedStream." } }
public void AddShare(IShare share) { try { if (!IsEnabled || !_redisProvider.IsConnected) { return; } //_client.StartPipe(); // batch the commands. // add the share to round var currentKey = $"{_coin}:shares:round:current"; var miner = (IStratumMiner)share.Miner; _redisProvider.Client.HIncrByFloat(currentKey, miner.Username, (double)share.Difficulty); //_redisProvider.Client.HIncrByFloat(currentKey, share.Miner.Username, share.Difficulty); // increment shares stats. var statsKey = $"{_coin}:stats"; _redisProvider.Client.HIncrBy(statsKey, share.IsValid ? "validShares" : "invalidShares", 1); // add to hashrate if (share.IsValid) { var hashrateKey = $"{_coin}:hashrate"; var randomModifier = Convert.ToString(miner.ValidShareCount, 16).PadLeft(8, '0'); string modifiedUsername = miner.Username + randomModifier; //var entry = $"{share.Difficulty}:{share.Miner.Username}"; var entry = string.Format("{0}:{1}", (double)miner.Difficulty, modifiedUsername); _redisProvider.Client.ZAdd(hashrateKey, Tuple.Create((double)TimeHelpers.NowInUnixTimestamp(), entry)); } //_client.EndPipe(); // execute the batch commands. } catch (Exception e) { _logger.Error("An exception occurred while committing share: {0:l}", e.Message); } }
public Share(IStratumMiner miner, UInt64 jobId, IJob job, string extraNonce2, string nTimeString, string nSolution) { Miner = miner; JobId = jobId; Job = job; Error = ShareError.None; var submitTime = TimeHelpers.NowInUnixTimestamp(); // 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 = extraNonce2; // set extraNonce2 for the share. // check size of miner supplied nTime. if (nTimeString.Length != 8) { Error = ShareError.IncorrectNTimeSize; return; } NTime = Convert.ToUInt32(nTimeString.HexToByteArray().ReverseBuffer().ToHexString(), 16); // read ntime for the share // make sure NTime is within range. if (NTime < job.BlockTemplate.CurTime || NTime > submitTime + 7200) { Error = ShareError.NTimeOutOfRange; return; } // 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); CoinbaseHash = Coin.Coinbase.Utils.HashCoinbase(CoinbaseBuffer); string nonceString = extraNonce2.HexToByteArray().ReverseBuffer().ToHexString() + ExtraNonce1.BigEndian().ToString("x8"); byte[] nonce = nonceString.HexToByteArray(); // create the merkle root. MerkleRoot = Job.MerkleTree.WithFirst(CoinbaseHash).ReverseBuffer(); // create the block headers { HeaderBuffer = Serializers.SerializeHeader(Job, MerkleRoot, nonce, NTime, nSolution.HexToByteArray().ReverseBuffer()); HeaderHash = Job.HashAlgorithm.Hash(HeaderBuffer); } 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.Options.IsProofOfStakeHybrid); 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. } }
public Share(IStratumMiner miner, UInt64 jobId, IJob job, string extraNonce2, string nTimeString, string nonceString, UInt32[] cycle) { Miner = miner; JobId = jobId; Job = job; Error = ShareError.None; Cycle = cycle; var submitTime = TimeHelpers.NowInUnixTimestamp(); // 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); HeaderValue = new BigInteger(HeaderHash); BlockHash = HeaderBuffer.DoubleDigest().ReverseBuffer(); if (!checkCycle()) { Error = ShareError.IncorrectCycle; return; } var _logger = Log.ForContext <Share>(); using (var stream = new MemoryStream()) { stream.WriteByte((byte)Cycle.Length); foreach (var edge in Cycle) { stream.WriteValueU32(edge); } CycleBuffer = stream.ToArray(); } CycleHash = Job.HashAlgorithm.Hash(CycleBuffer); CycleValue = new BigInteger(CycleHash); // calculate the share difficulty Difficulty = ((double)new BigRational(AlgorithmManager.Diff1, CycleValue)) * Job.HashAlgorithm.Multiplier; // calculate the block difficulty BlockDiffAdjusted = Job.Difficulty * Job.HashAlgorithm.Multiplier; // check if block candicate if (Job.Target >= CycleValue) { if (Difficulty < 0) { IsBlockCandidate = false; if (miner.Software == MinerSoftware.MeritMiner && miner.SoftwareVersion == new Version("0.1.0")) { // if we use merit-miner 0.1.0 diff can be negative Error = ShareError.NegativeDifficultyShareOutdatedMiner; } else { Error = ShareError.NegativeDifficultyShare; } return; } IsBlockCandidate = true; BlockHex = Serializers.SerializeBlock(Job, HeaderBuffer, CoinbaseBuffer, CycleBuffer, miner.Pool.Config.Coin.Options.IsProofOfStakeHybrid); } else { IsBlockCandidate = false; // 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 (miner.PreviousDifficulty > 0 && Difficulty >= miner.PreviousDifficulty) // if the difficulty matches miner's previous difficulty before the last vardiff triggered difficulty change { _logger.Debug("\tprevdiff lower; diff >= prevdiff: {0}::{1}", Difficulty, miner.PreviousDifficulty); 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. } }
public Share(IStratumMiner miner, UInt64 jobId, IJob job, string extraNonce2, string nTimeString, string nonceString) { _logger.Debug("Entering share constructor: {0}", nonceString); Miner = miner; JobId = jobId; Job = job; Error = ShareError.None; var submitTime = TimeHelpers.NowInUnixTimestamp(); // time we recieved the share from miner. if (Job == null) { _logger.Error("Job is null"); Error = ShareError.JobNotFound; return; } if (extraNonce2 == null) { _logger.Error("extraNonce2 is NULL!"); } // check size of miner supplied extraNonce2 if (extraNonce2.Length / 2 != ExtraNonce.ExpectedExtraNonce2Size) { _logger.Error("Incorrect Extranonce2 size: {0} while expecting {1}", extraNonce2.Length, ExtraNonce.ExpectedExtraNonce2Size * 2); Error = ShareError.IncorrectExtraNonce2Size; return; } ExtraNonce2 = Convert.ToUInt32(extraNonce2, 16); // set extraNonce2 for the share. if (nTimeString == null) { _logger.Error("nTimeString is NULL!"); } // check size of miner supplied nTime. if (nTimeString.Length != 8) { _logger.Error("nTimeString length !=8: {0}", nTimeString.Length); 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) { _logger.Error("NTime Out Of Range!"); Error = ShareError.NTimeOutOfRange; return; } if (nonceString == null) { _logger.Error("nonceString is NULL!"); } // check size of miner supplied nonce. if (nonceString.Length != 8) { _logger.Error("nonceString.Length != 8: {0}", nonceString.Length); Error = ShareError.IncorrectNonceSize; return; } Nonce = Convert.ToUInt32(nonceString, 16); // nonce supplied by the miner for the share. if (miner == null) { _logger.Error("miner is NULL!"); } // 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. { _logger.Error("Duplicate share: {0:l}", nonceString); Error = ShareError.DuplicateShare; return; } _logger.Debug("Serialize Share {0}", nonceString); // 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 _logger.Debug("Getting Header buffer for Share {0}", nonceString); HeaderBuffer = Serializers.SerializeHeader(Job, MerkleRoot, NTime, Nonce); HeaderHash = Job.HashAlgorithm.Hash(HeaderBuffer); _logger.Debug("Got share {0} of length: {1}\nPOW: {2,64:l}\nTGT: {3,64:l}", nonceString, HeaderHash.Length, HeaderHash.ReverseBytes().ToHexString(), Job.Target.ToByteArray().ReverseBytes().ToHexString() ); 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; /* * Test false pozitive block candidates: negative bigints were the problem * byte[] testbytes = new byte[] { * 0xf7, 0xdf, 0xed, 0xbd, * 0x9a, 0x2b, 0xa5, 0x1f, * 0x7b, 0x0d, 0x68, 0x76, * 0xbe, 0x1f, 0x18, 0xd6, * 0x2d, 0x49, 0x94, 0x91, * 0x69, 0x11, 0x39, 0x41, * 0xdf, 0x1f, 0x25, 0xdb, * 0x9b, 0x4e, 0x97, 0xb7 * }; * string teststr = testbytes.ReverseBuffer().ToHexString(); * HeaderValue = new BigInteger(testbytes); */ // check if block candicate if (Job.Target >= HeaderValue) //if (true) //for Debug only { IsBlockCandidate = true; BlockHex = Serializers.SerializeBlock(Job, HeaderBuffer, CoinbaseBuffer, miner.Pool.Config.Coin.Options.IsProofOfStakeHybrid); BlockHash = HeaderBuffer.DoubleDigest().ReverseBuffer(); try { _logger.Debug("Job.Target is greater than or equal HeaderValue(POW-SCRYPT)!!!:\n{9}\n{10}\n\n" + "Big-Endian values for Block Header:\n" + "job.BlockTemplate.Version={0}\n" + "job.PreviousBlockHash={1}\n" + "MerkleRoot={2}\n" + "NTime={3}\n" + "job.EncodedDifficulty={4}\n" + "Nonce={5}\n" + "==============\n" + "result={6}\n\n" + "Big-Endian:\n" + "BlockHex={7}\n" + "BlockHash(2xSHA256)={8}\n", job.BlockTemplate.Version, BitConverter.ToString(job.PreviousBlockHash.HexToByteArray()).Replace("-", string.Empty), BitConverter.ToString(MerkleRoot).Replace("-", string.Empty), NTime, job.EncodedDifficulty, Nonce, BitConverter.ToString(HeaderBuffer).Replace("-", string.Empty), BlockHex, BitConverter.ToString(BlockHash).Replace("-", string.Empty), Job.Target.ToByteArray().ReverseBuffer().ToHexString(), HeaderValue.ToByteArray().ReverseBuffer().ToHexString() ); } catch (Exception e) { _logger.Error(e, "Something has happened while logging"); } } 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. } }