/// <summary> /// Processes the share. /// </summary> /// <param name="miner">The miner.</param> /// <param name="jobId">The job identifier.</param> /// <param name="extraNonce2">The extra nonce2.</param> /// <param name="nTimeString">The n time string.</param> /// <param name="nonceString">The nonce string.</param> /// <returns></returns> public IShare ProcessShare(IStratumMiner miner, string jobId, string extraNonce2, string nTimeString, string nonceString, string cycle) { // check if the job exists var id = Convert.ToUInt64(jobId, 16); var job = _jobTracker.Get(id); string[] cycleEdgesStrs = cycle.Split(','); UInt32[] cycleEdges = new UInt32[cycleEdgesStrs.Length]; for (var i = 0; i < cycleEdgesStrs.Length; i++) { cycleEdges[i] = Convert.ToUInt32(cycleEdgesStrs[i], 16); } // create the share var share = new Share(miner, id, job, extraNonce2, nTimeString, nonceString, cycleEdges); if (share.IsValid) { HandleValidShare(share); } else { HandleInvalidShare(share); } OnShareSubmitted(new ShareEventArgs(miner)); // notify the listeners about the share. return(share); }
/// <summary> /// Processes the share. /// </summary> /// <param name="miner">The miner.</param> /// <param name="jobId">The job identifier.</param> /// <param name="extraNonce2">The extra nonce2.</param> /// <param name="nTimeString">The n time string.</param> /// <param name="nonceString">The nonce string.</param> /// <returns></returns> public IShare ProcessShare(IStratumMiner miner, string jobId, string extraNonce2, string nTimeString, string nonceString) { // check if the job exists var id = Convert.ToUInt64(jobId, 16); var job = _jobTracker.Get(id); _logger.Debug("TakeShare before share constructor: nonce={0:l}, extranonce2={1:l}", nonceString, extraNonce2); // create the share var share = new Share(miner, id, job, extraNonce2, nTimeString, nonceString); if (share.IsValid) { HandleValidShare(share); } else { HandleInvalidShare(share); } OnShareSubmitted(new ShareEventArgs(miner)); // notify the listeners about the share. return(share); }
public void UpdateDifficulty(IStratumMiner miner) { try { if (!IsEnabled) return; using (var connection = new MySqlConnection(_mySqlProvider.ConnectionString)) { connection.Execute( "UPDATE pool_worker SET difficulty = @difficulty WHERE username = @username", new { difficulty = miner.Difficulty, username = miner.Username }); } } catch (Exception e) { _logger.Error("An exception occured while updating difficulty for miner; {0:l}", e.Message); } }
/// <summary> /// Processes the share. /// </summary> /// <param name="miner">The miner.</param> /// <param name="jobId">The job identifier.</param> /// <param name="extraNonce2">The extra nonce2.</param> /// <param name="nTimeString">The n time string.</param> /// <param name="nonceString">The nonce string.</param> /// <returns></returns> public IShare ProcessShare(IStratumMiner miner, string jobId, string nTime, string extraNonce2, string equihashSolution) { // check if the job exists var id = Convert.ToUInt64(jobId, 16); var job = _jobTracker.Get(id); // create the share var share = new Share(miner, id, job, extraNonce2, nTime, equihashSolution); if (share.IsValid) { HandleValidShare(share); } else { HandleInvalidShare(share); } OnShareSubmitted(new ShareEventArgs(miner)); // notify the listeners about the share. return(share); }
/// <summary> /// Processes the share. /// </summary> /// <param name="miner">The miner.</param> /// <param name="jobId">The job identifier.</param> /// <param name="extraNonce2">The extra nonce2.</param> /// <param name="nTimeString">The n time string.</param> /// <param name="nonceString">The nonce string.</param> /// <returns></returns> public IShare ProcessShare(IStratumMiner miner, string jobId, string extraNonce2, string nTimeString, string nonceString, string jobPayoutUser) { // check if the job exists var id = Convert.ToUInt64(jobId, 16); var job = _jobTracker.Get(id); Share share; if (!job.MetronomeHash.Equals(MetronomeTracker.CurrentMetronomeHash) || BigInteger.Parse(MetronomeTracker.CurrentMetronomeHash, System.Globalization.NumberStyles.HexNumber).IsZero) { share = new Share(miner, id, null, extraNonce2, nTimeString, nonceString); share.Error = ShareError.InsideSleepWindow; } else { // create the share share = new Share(miner, id, job, extraNonce2, nTimeString, nonceString); } share.PayoutUser = jobPayoutUser; if (share.IsValid) { HandleValidShare(share); } else { HandleInvalidShare(share); } if (share.Error != ShareError.InsideSleepWindow) { OnShareSubmitted(new ShareEventArgs(miner)); // notify the listeners about the share. } return(share); }
public void UpdateDifficulty(IStratumMiner miner) { try { if (!IsEnabled) { return; } using (var connection = new MySqlConnection(_mySqlProvider.ConnectionString)) { connection.Execute( "UPDATE pool_worker SET difficulty = @difficulty WHERE username = @username", new { difficulty = miner.Difficulty, username = miner.Username }); } } catch (Exception e) { _logger.Error("An exception occured while updating difficulty for miner; {0:l}", e.Message); } }
/// <summary> /// Processes the share. /// </summary> /// <param name="miner">The miner.</param> /// <param name="jobId">The job identifier.</param> /// <param name="extraNonce2">The extra nonce2.</param> /// <param name="nTimeString">The n time string.</param> /// <param name="nonceString">The nonce string.</param> /// <returns></returns> public IShare ProcessShare(IStratumMiner miner, string jobId, string extraNonce2, string nTimeString, string nonceString) { // check if the job exists var id = Convert.ToUInt64(jobId, 16); var job = _jobTracker.Get(id); // create the share var share = new Share(miner, id, job, extraNonce2, nTimeString, nonceString); if (share.IsValid) HandleValidShare(share); else HandleInvalidShare(share); OnShareSubmitted(new ShareEventArgs(miner)); // notify the listeners about the share. return share; }
public ShareTests() { /* * -- create-generation start -- * rpcData: {"version":2,"previousblockhash":"1c4eb88e47564cb796b5c6648c74bec51d7215ac12fc4168b14827aac74a8062","transactions":[{"data":"010000000332a82e92f522deee69b09e27858ba9b87585f2a4913ef71018df40909032fdc3000000006a473044022019ca05cb880a04f0d842268b7e75ac6d2695fc544df033e3daeb29239251a8970220031f6336767f2ea617347484e1290ec0bdcc71056ea2d3084e75384905250ec50121030dd394118fb66ca288bff71d8ea762678783b005770f7f9ba4128233191e0847ffffffff086747cbd339b21b950774186091653a7b8f5751b00a906ff6f5561b3a6fcee6010000006b4830450221009ae1ba9a216d313cc592fc2c1ef08f1e0e555a32b6c1b305f685ac882d38356b0220243106bbb5bb76dde142e574cba8f30c1e2f7059e8e9161770396fbd2b50420f0121030dd394118fb66ca288bff71d8ea762678783b005770f7f9ba4128233191e0847ffffffffe2f15804b1e41c36c925c6f64f219b2bdb3c9fbff4c97a4f0e8c7f31d7e6f2af000000006b48304502200be8894fdd7f5c19be248a979c08bbf2395f606e038c3e02c0266474c03699ab022100ff5de87086e487410f5d7b68012655ca6d814f0caeb9ca42d9c425a90f68b3030121030dd394118fb66ca288bff71d8ea762678783b005770f7f9ba4128233191e0847ffffffff02a0f01900000000001976a9141c50209a1dfdf53313d237b75e9aeb553ca1dfda88ac00e1f505000000001976a914cbb9a3e7a7c1651b1006f876f08b40be85b274f588ac00000000","hash":"dc3a80ec6c45aa489453b2c4abf6761eb6656d949e26d01793458c166640e5f3","depends":[],"fee":0,"sigops":2}],"coinbaseaux":{"flags":"062f503253482f"},"coinbasevalue":5000000000,"target":"00000048d4f70000000000000000000000000000000000000000000000000000","mintime":1403691059,"mutable":["time","transactions","prevblock"],"noncerange":"00000000ffffffff","sigoplimit":20000,"sizelimit":1000000,"curtime":1403691825,"bits":"1d48d4f7","height":315152} * * -- scriptSigPart data -- * -> height: 315152 serialized: 0310cf04 * -> coinbase: 062f503253482f hex: 062f503253482f * -> date: 1403691824760 final:1403691824 serialized: 0430a3aa53 * -- p1 data -- * txVersion: 1 packed: 01000000 * txInputsCount: 1 varIntBuffer: 01 * txInPrevOutHash: 0 uint256BufferFromHash: 0000000000000000000000000000000000000000000000000000000000000000 * txInPrevOutIndex: 4294967295 packUInt32LE: ffffffff * scriptSigPart1.length: 17 extraNoncePlaceholder.length:8 scriptSigPart2.length:14 all: 39 varIntBuffer: 27 * scriptSigPart1: 0310cf04062f503253482f0430a3aa5308 * p1: 01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff270310cf04062f503253482f0430a3aa5308 * -- generateOutputTransactions -- * block-reward: 5000000000 * recipient-reward: 50000000 packInt64LE: 80f0fa0200000000 * lenght: 25 varIntBuffer: 19 * script: 76a9147d576fbfca48b899dc750167dd2a2a6572fff49588ac * pool-reward: 4950000000 packInt64LE: 80010b2701000000 * lenght: 25 varIntBuffer: 19 * script: 76a914329035234168b8da5af106ceb20560401236849888ac * txOutputBuffers.lenght : 2 varIntBuffer: 02 * -- p2 -- * scriptSigPart2: 0d2f6e6f64655374726174756d2f * txInSequence: 0 packUInt32LE: 00000000 * outputTransactions: 0280010b27010000001976a914329035234168b8da5af106ceb20560401236849888ac80f0fa02000000001976a9147d576fbfca48b899dc750167dd2a2a6572fff49588ac * txLockTime: 0 packUInt32LE: 00000000 * txComment: * p2: 0d2f6e6f64655374726174756d2f000000000280010b27010000001976a914329035234168b8da5af106ceb20560401236849888ac80f0fa02000000001976a9147d576fbfca48b899dc750167dd2a2a6572fff49588ac00000000 * * getJobParams: ["2","c74a8062b14827aa12fc41681d7215ac8c74bec596b5c66447564cb71c4eb88e","01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff270310cf04062f503253482f0430a3aa5308","0d2f6e6f64655374726174756d2f000000000280010b27010000001976a914329035234168b8da5af106ceb20560401236849888ac80f0fa02000000001976a9147d576fbfca48b899dc750167dd2a2a6572fff49588ac00000000",["f3e54066168c459317d0269e946d65b61e76f6abc4b2539448aa456cec803adc"],"00000002","1d48d4f7","53aaa331",true] */ // daemon client _daemonClient = Substitute.For <IDaemonClient>(); _daemonClient.ValidateAddress(Arg.Any <string>()).Returns(new ValidateAddress { IsValid = true }); // block template const string json = "{\"result\":{\"version\":2,\"previousblockhash\":\"1c4eb88e47564cb796b5c6648c74bec51d7215ac12fc4168b14827aac74a8062\",\"transactions\":[{\"data\":\"010000000332a82e92f522deee69b09e27858ba9b87585f2a4913ef71018df40909032fdc3000000006a473044022019ca05cb880a04f0d842268b7e75ac6d2695fc544df033e3daeb29239251a8970220031f6336767f2ea617347484e1290ec0bdcc71056ea2d3084e75384905250ec50121030dd394118fb66ca288bff71d8ea762678783b005770f7f9ba4128233191e0847ffffffff086747cbd339b21b950774186091653a7b8f5751b00a906ff6f5561b3a6fcee6010000006b4830450221009ae1ba9a216d313cc592fc2c1ef08f1e0e555a32b6c1b305f685ac882d38356b0220243106bbb5bb76dde142e574cba8f30c1e2f7059e8e9161770396fbd2b50420f0121030dd394118fb66ca288bff71d8ea762678783b005770f7f9ba4128233191e0847ffffffffe2f15804b1e41c36c925c6f64f219b2bdb3c9fbff4c97a4f0e8c7f31d7e6f2af000000006b48304502200be8894fdd7f5c19be248a979c08bbf2395f606e038c3e02c0266474c03699ab022100ff5de87086e487410f5d7b68012655ca6d814f0caeb9ca42d9c425a90f68b3030121030dd394118fb66ca288bff71d8ea762678783b005770f7f9ba4128233191e0847ffffffff02a0f01900000000001976a9141c50209a1dfdf53313d237b75e9aeb553ca1dfda88ac00e1f505000000001976a914cbb9a3e7a7c1651b1006f876f08b40be85b274f588ac00000000\",\"hash\":\"dc3a80ec6c45aa489453b2c4abf6761eb6656d949e26d01793458c166640e5f3\",\"depends\":[],\"fee\":0,\"sigops\":2}],\"coinbaseaux\":{\"flags\":\"062f503253482f\"},\"coinbasevalue\":5000000000,\"target\":\"00000048d4f70000000000000000000000000000000000000000000000000000\",\"mintime\":1403691059,\"mutable\":[\"time\",\"transactions\",\"prevblock\"],\"noncerange\":\"00000000ffffffff\",\"sigoplimit\":20000,\"sizelimit\":1000000,\"curtime\":1403691825,\"bits\":\"1d48d4f7\",\"height\":315152},\"error\":null,\"id\":1}"; var blockTemplateObject = JsonConvert.DeserializeObject <DaemonResponse <BlockTemplate> >(json); _blockTemplate = blockTemplateObject.Result; // extra nonce _extraNonce = Substitute.For <ExtraNonce>((UInt32)0); // signature script _signatureScript = Substitute.For <SignatureScript>( _blockTemplate.Height, _blockTemplate.CoinBaseAux.Flags, 1403691824760, (byte)_extraNonce.ExtraNoncePlaceholder.Length, "/nodeStratum/"); // outputs _outputs = Substitute.For <Outputs>(_daemonClient); double blockReward = 5000000000; // the amount rewarded by the block. // sample reward recipient var rewardsConfig = Substitute.For <IRewardsConfig>(); var amount = blockReward * 0.01; blockReward -= amount; var rewards = new Dictionary <string, float> { { "mrwhWEDnU6dUtHZJ2oBswTpEdbBHgYiMji", (float)amount } }; rewardsConfig.GetEnumerator().Returns(rewards.GetEnumerator()); foreach (var pair in rewards) { _outputs.AddRecipient(pair.Key, pair.Value); } // sample pool wallet var walletConfig = Substitute.For <IWalletConfig>(); walletConfig.Adress.Returns("mk8JqN1kNWju8o3DXEijiJyn7iqkwktAWq"); _outputs.AddPoolWallet(walletConfig.Adress, blockReward); var metaConfig = Substitute.For <IMetaConfig>(); // generation transaction _generationTransaction = Substitute.For <GenerationTransaction>(_extraNonce, _daemonClient, _blockTemplate, walletConfig, rewardsConfig, metaConfig, false); //_generationTransaction.Inputs.First().SignatureScript = _signatureScript; //_generationTransaction.Outputs = _outputs; _generationTransaction.Create(); // hash algorithm _hashAlgorithm = Substitute.For <Scrypt>(); // the job. _job = new Job(2, _hashAlgorithm, _blockTemplate, _generationTransaction) { CleanJobs = true }; // the job tracker. _jobTracker = Substitute.For <IJobTracker>(); _jobTracker.Get(2).Returns(_job); // the job manager. _jobManager = Substitute.For <IJobManager>(); _jobManager.ExtraNonce.Next().Returns((UInt32)0x58000000); // coin config _miner = Substitute.For <IStratumMiner>(); _miner.ExtraNonce.Returns((UInt32)0x58000000); }
public void UpdateDifficulty(IStratumMiner miner) { return; }
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. } }
public void UpdateDifficulty(IStratumMiner miner) { // as we don't have an actual persistance layer, we can't write difficulty update information. return; }
public StratumContext(IStratumMiner miner) { Miner = miner; }
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.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); // 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 ShareEventArgs(IStratumMiner miner) { Miner = miner; }
public Share(IStratumMiner miner, UInt64 jobId, IJob job, string extraNonce2, string nTimeString, string nonceString) { Miner = miner; JobId = jobId; Job = job; Error = ShareError.None; // TODO: add extranonce2 size check!. // TODO: add duplicate share check. // TODO: add nTime out of range check if (Job == null) { Error = ShareError.JobNotFound; Log.ForContext<Share>().Warning("Job doesn't exist: {0}", JobId); return; } // check miner supplied nTime. if (nTimeString.Length != 8) { Error = ShareError.IncorrectNTimeSize; Log.ForContext<Share>().Warning("Incorrect size of nTime"); return; } NTime = Convert.ToUInt32(nTimeString, 16); // ntime for the share // check miner supplied nonce. if (nonceString.Length != 8) { Error = ShareError.IncorrectNonceSize; Log.ForContext<Share>().Warning("incorrect size of nonce"); return; } Nonce = Convert.ToUInt32(nonceString, 16); // nonce supplied by the miner for the share. // check miner supplied extraNonce2 ExtraNonce2 = Convert.ToUInt32(extraNonce2, 16); // check job supplied parameters. Height = job.BlockTemplate.Height; ExtraNonce1 = miner.ExtraNonce; // extra nonce1 assigned to miner. // 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(Algorithms.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); BlockHash = HeaderBuffer.DoubleDigest().ReverseBuffer(); // TODO: make sure this is okay! } else { IsBlockCandidate = false; BlockHash = HeaderBuffer.DoubleDigest().ReverseBuffer(); // Check if share difficulty reaches miner difficulty. if (Difficulty / 16 < 0.99) { // todo: add low difficulty share check. } } }
public ShareTests() { /* -- create-generation start -- rpcData: {"version":2,"previousblockhash":"1c4eb88e47564cb796b5c6648c74bec51d7215ac12fc4168b14827aac74a8062","transactions":[{"data":"010000000332a82e92f522deee69b09e27858ba9b87585f2a4913ef71018df40909032fdc3000000006a473044022019ca05cb880a04f0d842268b7e75ac6d2695fc544df033e3daeb29239251a8970220031f6336767f2ea617347484e1290ec0bdcc71056ea2d3084e75384905250ec50121030dd394118fb66ca288bff71d8ea762678783b005770f7f9ba4128233191e0847ffffffff086747cbd339b21b950774186091653a7b8f5751b00a906ff6f5561b3a6fcee6010000006b4830450221009ae1ba9a216d313cc592fc2c1ef08f1e0e555a32b6c1b305f685ac882d38356b0220243106bbb5bb76dde142e574cba8f30c1e2f7059e8e9161770396fbd2b50420f0121030dd394118fb66ca288bff71d8ea762678783b005770f7f9ba4128233191e0847ffffffffe2f15804b1e41c36c925c6f64f219b2bdb3c9fbff4c97a4f0e8c7f31d7e6f2af000000006b48304502200be8894fdd7f5c19be248a979c08bbf2395f606e038c3e02c0266474c03699ab022100ff5de87086e487410f5d7b68012655ca6d814f0caeb9ca42d9c425a90f68b3030121030dd394118fb66ca288bff71d8ea762678783b005770f7f9ba4128233191e0847ffffffff02a0f01900000000001976a9141c50209a1dfdf53313d237b75e9aeb553ca1dfda88ac00e1f505000000001976a914cbb9a3e7a7c1651b1006f876f08b40be85b274f588ac00000000","hash":"dc3a80ec6c45aa489453b2c4abf6761eb6656d949e26d01793458c166640e5f3","depends":[],"fee":0,"sigops":2}],"coinbaseaux":{"flags":"062f503253482f"},"coinbasevalue":5000000000,"target":"00000048d4f70000000000000000000000000000000000000000000000000000","mintime":1403691059,"mutable":["time","transactions","prevblock"],"noncerange":"00000000ffffffff","sigoplimit":20000,"sizelimit":1000000,"curtime":1403691825,"bits":"1d48d4f7","height":315152} -- scriptSigPart data -- -> height: 315152 serialized: 0310cf04 -> coinbase: 062f503253482f hex: 062f503253482f -> date: 1403691824760 final:1403691824 serialized: 0430a3aa53 -- p1 data -- txVersion: 1 packed: 01000000 txInputsCount: 1 varIntBuffer: 01 txInPrevOutHash: 0 uint256BufferFromHash: 0000000000000000000000000000000000000000000000000000000000000000 txInPrevOutIndex: 4294967295 packUInt32LE: ffffffff scriptSigPart1.length: 17 extraNoncePlaceholder.length:8 scriptSigPart2.length:14 all: 39 varIntBuffer: 27 scriptSigPart1: 0310cf04062f503253482f0430a3aa5308 p1: 01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff270310cf04062f503253482f0430a3aa5308 -- generateOutputTransactions -- block-reward: 5000000000 recipient-reward: 50000000 packInt64LE: 80f0fa0200000000 lenght: 25 varIntBuffer: 19 script: 76a9147d576fbfca48b899dc750167dd2a2a6572fff49588ac pool-reward: 4950000000 packInt64LE: 80010b2701000000 lenght: 25 varIntBuffer: 19 script: 76a914329035234168b8da5af106ceb20560401236849888ac txOutputBuffers.lenght : 2 varIntBuffer: 02 -- p2 -- scriptSigPart2: 0d2f6e6f64655374726174756d2f txInSequence: 0 packUInt32LE: 00000000 outputTransactions: 0280010b27010000001976a914329035234168b8da5af106ceb20560401236849888ac80f0fa02000000001976a9147d576fbfca48b899dc750167dd2a2a6572fff49588ac txLockTime: 0 packUInt32LE: 00000000 txComment: p2: 0d2f6e6f64655374726174756d2f000000000280010b27010000001976a914329035234168b8da5af106ceb20560401236849888ac80f0fa02000000001976a9147d576fbfca48b899dc750167dd2a2a6572fff49588ac00000000 getJobParams: ["2","c74a8062b14827aa12fc41681d7215ac8c74bec596b5c66447564cb71c4eb88e","01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff270310cf04062f503253482f0430a3aa5308","0d2f6e6f64655374726174756d2f000000000280010b27010000001976a914329035234168b8da5af106ceb20560401236849888ac80f0fa02000000001976a9147d576fbfca48b899dc750167dd2a2a6572fff49588ac00000000",["f3e54066168c459317d0269e946d65b61e76f6abc4b2539448aa456cec803adc"],"00000002","1d48d4f7","53aaa331",true] */ // daemon client _daemonClient = Substitute.For<IDaemonClient>(); _daemonClient.ValidateAddress(Arg.Any<string>()).Returns(new ValidateAddress { IsValid = true }); // block template const string json = "{\"result\":{\"version\":1,\"previousblockhash\":\"1c4eb88e47564cb796b5c6648c74bec51d7215ac12fc4168b14827aac74a8062\",\"transactions\":[{\"data\":\"010000000332a82e92f522deee69b09e27858ba9b87585f2a4913ef71018df40909032fdc3000000006a473044022019ca05cb880a04f0d842268b7e75ac6d2695fc544df033e3daeb29239251a8970220031f6336767f2ea617347484e1290ec0bdcc71056ea2d3084e75384905250ec50121030dd394118fb66ca288bff71d8ea762678783b005770f7f9ba4128233191e0847ffffffff086747cbd339b21b950774186091653a7b8f5751b00a906ff6f5561b3a6fcee6010000006b4830450221009ae1ba9a216d313cc592fc2c1ef08f1e0e555a32b6c1b305f685ac882d38356b0220243106bbb5bb76dde142e574cba8f30c1e2f7059e8e9161770396fbd2b50420f0121030dd394118fb66ca288bff71d8ea762678783b005770f7f9ba4128233191e0847ffffffffe2f15804b1e41c36c925c6f64f219b2bdb3c9fbff4c97a4f0e8c7f31d7e6f2af000000006b48304502200be8894fdd7f5c19be248a979c08bbf2395f606e038c3e02c0266474c03699ab022100ff5de87086e487410f5d7b68012655ca6d814f0caeb9ca42d9c425a90f68b3030121030dd394118fb66ca288bff71d8ea762678783b005770f7f9ba4128233191e0847ffffffff02a0f01900000000001976a9141c50209a1dfdf53313d237b75e9aeb553ca1dfda88ac00e1f505000000001976a914cbb9a3e7a7c1651b1006f876f08b40be85b274f588ac00000000\",\"hash\":\"dc3a80ec6c45aa489453b2c4abf6761eb6656d949e26d01793458c166640e5f3\",\"depends\":[],\"fee\":0,\"sigops\":2}],\"coinbaseaux\":{\"flags\":\"062f503253482f\"},\"coinbasevalue\":5000000000,\"target\":\"00000048d4f70000000000000000000000000000000000000000000000000000\",\"mintime\":1403691059,\"mutable\":[\"time\",\"transactions\",\"prevblock\"],\"noncerange\":\"00000000ffffffff\",\"sigoplimit\":20000,\"sizelimit\":1000000,\"curtime\":1403691825,\"bits\":\"1d48d4f7\",\"height\":315152},\"error\":null,\"id\":1}"; var blockTemplateObject = JsonConvert.DeserializeObject<DaemonResponse<BlockTemplate>>(json); _blockTemplate = blockTemplateObject.Result; // extra nonce _extraNonce = Substitute.For<ExtraNonce>((UInt32)0); // signature script _signatureScript = Substitute.For<SignatureScript>( _blockTemplate.Height, _blockTemplate.CoinBaseAux.Flags, 1403691824760, (byte)_extraNonce.ExtraNoncePlaceholder.Length, "/nodeStratum/"); // pool config var poolConfig = Substitute.For<IPoolConfig>(); // create coin config. var coinConfig = Substitute.For<ICoinConfig>(); coinConfig.Options.TxMessageSupported.Returns(false); coinConfig.Options.IsProofOfStakeHybrid.Returns(false); poolConfig.Coin.Returns(coinConfig); // outputs _outputs = Substitute.For<Outputs>(_daemonClient, coinConfig); double blockReward = 5000000000; // the amount rewarded by the block. // create rewards config. var rewardsConfig = Substitute.For<IRewardsConfig>(); poolConfig.Rewards.Returns(rewardsConfig); var amount = blockReward * 0.01; blockReward -= amount; var rewards = new Dictionary<string, float> { { "mrwhWEDnU6dUtHZJ2oBswTpEdbBHgYiMji", (float)amount } }; rewardsConfig.GetEnumerator().Returns(rewards.GetEnumerator()); foreach (var pair in rewards) { _outputs.AddRecipient(pair.Key, pair.Value); } // create wallet config. var walletConfig = Substitute.For<IWalletConfig>(); poolConfig.Wallet.Returns(walletConfig); // create sample pool central wallet output. walletConfig.Adress.Returns("mk8JqN1kNWju8o3DXEijiJyn7iqkwktAWq"); _outputs.AddPoolWallet(walletConfig.Adress, blockReward); // generation transaction _generationTransaction = Substitute.For<GenerationTransaction>(_extraNonce, _daemonClient, _blockTemplate, poolConfig); _generationTransaction.Inputs.First().SignatureScript = _signatureScript; _generationTransaction.Outputs = _outputs; _generationTransaction.Create(); // hash algorithm _hashAlgorithm = new Scrypt(); // the job. _job = new Job(2,_hashAlgorithm, _blockTemplate, _generationTransaction) { CleanJobs = true }; // the job tracker. _jobTracker = Substitute.For<IJobTracker>(); _jobTracker.Get(2).Returns(_job); // the job manager. _jobManager = Substitute.For<IJobManager>(); _jobManager.ExtraNonce.Next().Returns((UInt32)0x58000000); // coin config _miner = Substitute.For<IStratumMiner>(); _miner.ExtraNonce.Returns((UInt32)0x58000000); }
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 void UpdateDifficulty(IStratumMiner miner) { // with-in our current hybrid-storage-layer, we don't need to write difficulty to persistance layer. return; }