diff --git a/Changelog.md b/Changelog.md index bfe774e62..20c242e68 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,28 @@ +##### [v0.1.5 alpha](https://github.com/CoiniumServ/CoiniumServ/releases/tag/v0.1.5-alpha) - 05.09.2014 + +**Payments** +* Fixed a bug in hybrid-storage layer where blocks were not correctly set as confirmed once they were actually so. + +**Web** +* Fixed a bug in embedded web-server where some users were not able to use the interface. +* Updated web-site tempaltes which reflects newest API changes. + +**Storage** +* Fixed a bug in migration-manager where if it couldn't connect to MySQL would cause program to crash & terminate. + +**API** +* Improved pool API. + +**Pools** +* Pools can now determine if connection to coin network is healthy. +* Fixed hashrate calculation bug. + +**Jobs** +* JobTracker can now clean expired jobs. + +**Configuration** +* Moved config.json "website:stats" section to upper level and renamed as "statistics". You have to apply the change to your existing config.json file! + ##### [v0.1.4 alpha](https://github.com/CoiniumServ/CoiniumServ/releases/tag/v0.1.4-alpha) - 03.09.2014 **Storage** diff --git a/README.md b/README.md index d8fa41503..c8ab4a1c1 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ You can send tips and furher support the project or get tips for contributing by ### Status -[v0.1.4 alpha](https://github.com/CoiniumServ/CoiniumServ/releases/tag/v0.1.4-alpha) released +[v0.1.5 alpha](https://github.com/CoiniumServ/CoiniumServ/releases/tag/v0.1.5-alpha) released ### Features @@ -122,7 +122,7 @@ CoiniumServ supports storage layer interfaces that you can extend to implement y * __MPOS Layer__: a compatibility layer based on mysql that supports MPOS whenever you want payments to be handled by MPOS. ###### Development Model -* Strictly [follows](https://github.com/CoiniumServ/CoiniumServ/tree/develop/src/Tests) the [Test Driven Development](http://en.wikipedia.org/wiki/Test-driven_development) model. We have implemented extensive [tests](https://github.com/CoiniumServ/CoiniumServ/tree/develop/src/Tests) for all important functionality and never merge in code that breaks tests and stuff. Yet again, when a new functionality is introduced we also expect proper tests to be implemented within the PR. In simple words, most probably you won't notice any functionality-breaking changes within the repository. +* We have implemented extensive [tests](https://github.com/CoiniumServ/CoiniumServ/tree/develop/src/Tests) for all important functionality and never merge in code that breaks tests and stuff. Yet again, when a new functionality is introduced we also expect proper tests to be implemented within the PR. In simple words, most probably you won't notice any functionality-breaking changes within the repository. * A strict ruleset for the [Development Model](https://github.com/CoiniumServ/CoiniumServ/wiki/Development-Model). You can follow our bleeding-edge [Develop](https://github.com/CoiniumServ/CoiniumServ) branch or stay with-in the stable [Master](https://github.com/CoiniumServ/CoiniumServ/tree/master) branch. diff --git a/src/CoiniumServ/App.config b/src/CoiniumServ/App.config index 547ad3c5c..4bc915578 100644 --- a/src/CoiniumServ/App.config +++ b/src/CoiniumServ/App.config @@ -5,19 +5,12 @@
+ - - - - - - - - - - + + @@ -26,4 +19,12 @@ - + + + + + + + + + diff --git a/src/CoiniumServ/CoiniumServ.csproj b/src/CoiniumServ/CoiniumServ.csproj index 4a2a75fcf..5a0beb5c0 100644 --- a/src/CoiniumServ/CoiniumServ.csproj +++ b/src/CoiniumServ/CoiniumServ.csproj @@ -211,9 +211,9 @@ - + - + @@ -848,6 +848,7 @@ PreserveNewest + PreserveNewest diff --git a/src/CoiniumServ/Configuration/ConfigManager.cs b/src/CoiniumServ/Configuration/ConfigManager.cs index db33d1ca7..a80d22f3b 100644 --- a/src/CoiniumServ/Configuration/ConfigManager.cs +++ b/src/CoiniumServ/Configuration/ConfigManager.cs @@ -29,6 +29,7 @@ using CoiniumServ.Logging; using CoiniumServ.Pools; using CoiniumServ.Server.Web; +using CoiniumServ.Statistics; using CoiniumServ.Utils.Helpers.IO; using Serilog; @@ -36,9 +37,10 @@ namespace CoiniumServ.Configuration { public class ConfigManager:IConfigManager { + public IStackConfig StackConfig { get; private set; } + public IStatisticsConfig StatisticsConfig { get; private set; } public IWebServerConfig WebServerConfig { get; private set; } public ILogConfig LogConfig { get; private set; } - public IStackConfig StackConfig { get; private set; } public List PoolConfigs { get; private set; } private const string GlobalConfigFilename = "config/config.json"; // global config filename. @@ -76,6 +78,7 @@ private void ReadGlobalConfig() } StackConfig = new StackConfig(data.stack); + StatisticsConfig = new StatisticsConfig(data.statistics); WebServerConfig = new WebServerConfig(data.website); LogConfig = new LogConfig(data.logging); } diff --git a/src/CoiniumServ/Configuration/IConfigManager.cs b/src/CoiniumServ/Configuration/IConfigManager.cs index 98fa4f1e2..791440e45 100644 --- a/src/CoiniumServ/Configuration/IConfigManager.cs +++ b/src/CoiniumServ/Configuration/IConfigManager.cs @@ -24,17 +24,20 @@ using CoiniumServ.Logging; using CoiniumServ.Pools; using CoiniumServ.Server.Web; +using CoiniumServ.Statistics; namespace CoiniumServ.Configuration { public interface IConfigManager { + IStackConfig StackConfig { get; } + + IStatisticsConfig StatisticsConfig { get; } + IWebServerConfig WebServerConfig { get; } ILogConfig LogConfig { get; } - IStackConfig StackConfig { get; } - List PoolConfigs { get; } void Initialize(); diff --git a/src/CoiniumServ/Container/Registries/ClassRegistry.cs b/src/CoiniumServ/Container/Registries/ClassRegistry.cs index 41a5844a2..8dfccc9a6 100644 --- a/src/CoiniumServ/Container/Registries/ClassRegistry.cs +++ b/src/CoiniumServ/Container/Registries/ClassRegistry.cs @@ -45,18 +45,15 @@ public ClassRegistry(IApplicationContext applicationContext) public void RegisterInstances() { - // pool + // per-pool objects _applicationContext.Container.Register().AsMultiInstance(); _applicationContext.Container.Register().AsMultiInstance(); _applicationContext.Container.Register().AsMultiInstance(); _applicationContext.Container.Register().AsMultiInstance(); _applicationContext.Container.Register().AsMultiInstance(); - _applicationContext.Container.Register().AsMultiInstance(); + _applicationContext.Container.Register().AsMultiInstance(); _applicationContext.Container.Register().AsMultiInstance(); - // statistics - _applicationContext.Container.Register().AsSingleton(); - // config _applicationContext.Container.Register().AsMultiInstance(); _applicationContext.Container.Register().AsMultiInstance(); diff --git a/src/CoiniumServ/Container/Registries/ManagerRegistry.cs b/src/CoiniumServ/Container/Registries/ManagerRegistry.cs index b78980786..c75439867 100644 --- a/src/CoiniumServ/Container/Registries/ManagerRegistry.cs +++ b/src/CoiniumServ/Container/Registries/ManagerRegistry.cs @@ -29,6 +29,7 @@ using CoiniumServ.Miners; using CoiniumServ.Pools; using CoiniumServ.Shares; +using CoiniumServ.Statistics; using CoiniumServ.Vardiff; namespace CoiniumServ.Container.Registries @@ -44,16 +45,20 @@ public ManagerRegistry(IApplicationContext applicationContext) public void RegisterInstances() { + // global singleton managers. + _applicationContext.Container.Register().AsSingleton(); + _applicationContext.Container.Register().AsSingleton(); + _applicationContext.Container.Register().AsSingleton(); + _applicationContext.Container.Register().AsSingleton(); + _applicationContext.Container.Register().AsSingleton(); + + // per-pool managers _applicationContext.Container.Register().AsMultiInstance(); _applicationContext.Container.Register().AsMultiInstance(); _applicationContext.Container.Register().AsMultiInstance(); _applicationContext.Container.Register().AsMultiInstance(); - _applicationContext.Container.Register().AsSingleton(); _applicationContext.Container.Register().AsMultiInstance(); - _applicationContext.Container.Register().AsMultiInstance(); - _applicationContext.Container.Register().AsSingleton(); - _applicationContext.Container.Register().AsSingleton(); - _applicationContext.Container.Register().AsSingleton(); + _applicationContext.Container.Register().AsMultiInstance(); } } } diff --git a/src/CoiniumServ/Container/Registries/StorageRegistry.cs b/src/CoiniumServ/Container/Registries/StorageRegistry.cs index ffaf9a895..5528cf64c 100644 --- a/src/CoiniumServ/Container/Registries/StorageRegistry.cs +++ b/src/CoiniumServ/Container/Registries/StorageRegistry.cs @@ -21,11 +21,9 @@ // #endregion using CoiniumServ.Container.Context; -using CoiniumServ.Persistance; using CoiniumServ.Persistance.Layers; using CoiniumServ.Persistance.Layers.Empty; using CoiniumServ.Persistance.Layers.Hybrid; -using CoiniumServ.Persistance.Layers.Hybrid.Migrations; using CoiniumServ.Persistance.Layers.Mpos; using CoiniumServ.Persistance.Providers; using CoiniumServ.Persistance.Providers.MySql; diff --git a/src/CoiniumServ/Factories/IObjectFactory.cs b/src/CoiniumServ/Factories/IObjectFactory.cs index d3676f099..1ce0efb5c 100644 --- a/src/CoiniumServ/Factories/IObjectFactory.cs +++ b/src/CoiniumServ/Factories/IObjectFactory.cs @@ -51,21 +51,17 @@ namespace CoiniumServ.Factories /// public interface IObjectFactory { - #region hash algorithms + #region global objects + IPoolManager GetPoolManager(); - /// - /// Returns instance of the given hash algorithm - /// - /// - /// - IHashAlgorithm GetHashAlgorithm(string algorithm); + IStatisticsManager GetStatisticsManager(); + + ILogManager GetLogManager(); #endregion #region pool objects - IPoolManager GetPoolManager(); - IPool GetPool(IPoolConfig poolConfig); /// @@ -76,10 +72,9 @@ public interface IObjectFactory IMinerManager GetMinerManager(IPoolConfig poolConfig, IStorageLayer storageLayer); - IJobManager GetJobManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJobTracker jobTracker, IShareManager shareManager, - IMinerManager minerManager, IHashAlgorithm hashAlgorithm); + IJobManager GetJobManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJobTracker jobTracker, IShareManager shareManager, IMinerManager minerManager, IHashAlgorithm hashAlgorithm); - IJobTracker GetJobTracker(); + IJobTracker GetJobTracker(IPoolConfig poolConfig); IShareManager GetShareManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJobTracker jobTracker, IStorageLayer storageLayer, IBlockProcessor blockProcessor); @@ -91,26 +86,26 @@ IJobManager GetJobManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJ IVardiffManager GetVardiffManager(IPoolConfig poolConfig, IShareManager shareManager); - INetworkStats GetNetworkStats(IDaemonClient daemonClient); - - IAlgorithmManager GetAlgorithmManager(IPoolManager poolManager); + INetworkInfo GetNetworkInfo(IDaemonClient daemonClient, IHashAlgorithm hashAlgorithm, IPoolConfig poolConfig); IBlocksCache GetBlocksCache(IStorageLayer storageLayer); - IStatisticsManager GetStatisticsManager(); - - #endregion + IMiningServer GetMiningServer(string type, IPoolConfig poolConfig, IPool pool, IMinerManager minerManager, IJobManager jobManager,IBanManager banManager); - #region server & service objects + IRpcService GetMiningService(string type, IPoolConfig poolConfig, IShareManager shareManager, IDaemonClient daemonClient); - IMiningServer GetMiningServer(string type, IPoolConfig poolConfig, IPool pool, IMinerManager minerManager, IJobManager jobManager, - IBanManager banManager); + #endregion - IRpcService GetMiningService(string type, IPoolConfig poolConfig, IShareManager shareManager, IDaemonClient daemonClient); + #region hash algorithms - IWebServer GetWebServer(); + /// + /// Returns instance of the given hash algorithm + /// + /// + /// + IHashAlgorithm GetHashAlgorithm(string algorithm); - INancyBootstrapper GetWebBootstrapper(); + IAlgorithmManager GetAlgorithmManager(IPoolManager poolManager); #endregion @@ -124,9 +119,11 @@ IMiningServer GetMiningServer(string type, IPoolConfig poolConfig, IPool pool, I #endregion - #region other objects + #region web-server objects - ILogManager GetLogManager(); + IWebServer GetWebServer(); + + INancyBootstrapper GetWebBootstrapper(); IMetricsManager GetMetricsManager(); diff --git a/src/CoiniumServ/Factories/ObjectFactory.cs b/src/CoiniumServ/Factories/ObjectFactory.cs index 905f61283..20dbcaa1f 100644 --- a/src/CoiniumServ/Factories/ObjectFactory.cs +++ b/src/CoiniumServ/Factories/ObjectFactory.cs @@ -71,23 +71,27 @@ public ObjectFactory(IApplicationContext applicationContext) #endregion - #region hash algorithms + #region global objects - /// - /// Returns instance of the given hash algorithm. - /// - /// - /// - public IHashAlgorithm GetHashAlgorithm(string algorithm) + public IPoolManager GetPoolManager() { - return _applicationContext.Container.Resolve(algorithm); + return _applicationContext.Container.Resolve(); } - public IPoolManager GetPoolManager() + public IStatisticsManager GetStatisticsManager() { - return _applicationContext.Container.Resolve(); + return _applicationContext.Container.Resolve(); + } + + public ILogManager GetLogManager() + { + return _applicationContext.Container.Resolve(); } + #endregion + + #region pool objects + public IPool GetPool(IPoolConfig poolConfig) { var @params = new NamedParameterOverloads @@ -98,10 +102,6 @@ public IPool GetPool(IPoolConfig poolConfig) return _applicationContext.Container.Resolve(@params); } - #endregion - - #region pool objects - /// /// Returns a new instance of daemon client. /// @@ -143,9 +143,14 @@ public IJobManager GetJobManager(IPoolConfig poolConfig, IDaemonClient daemonCli return _applicationContext.Container.Resolve(@params); } - public IJobTracker GetJobTracker() + public IJobTracker GetJobTracker(IPoolConfig poolConfig) { - return _applicationContext.Container.Resolve(); + var @params = new NamedParameterOverloads + { + {"poolConfig", poolConfig}, + }; + + return _applicationContext.Container.Resolve(@params); } public IShareManager GetShareManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJobTracker jobTracker, IStorageLayer storageLayer, IBlockProcessor blockProcessor) @@ -208,19 +213,16 @@ public IVardiffManager GetVardiffManager(IPoolConfig poolConfig, IShareManager s return _applicationContext.Container.Resolve(@params); } - public INetworkStats GetNetworkStats(IDaemonClient daemonClient) + public INetworkInfo GetNetworkInfo(IDaemonClient daemonClient, IHashAlgorithm hashAlgorithm, IPoolConfig poolConfig) { var @params = new NamedParameterOverloads { {"daemonClient", daemonClient}, + {"hashAlgorithm", hashAlgorithm}, + {"poolConfig", poolConfig}, }; - return _applicationContext.Container.Resolve(@params); - } - - public IAlgorithmManager GetAlgorithmManager(IPoolManager poolManager) - { - return _applicationContext.Container.Resolve(); + return _applicationContext.Container.Resolve(@params); } public IBlocksCache GetBlocksCache(IStorageLayer storageLayer) @@ -233,17 +235,7 @@ public IBlocksCache GetBlocksCache(IStorageLayer storageLayer) return _applicationContext.Container.Resolve(@params); } - public IStatisticsManager GetStatisticsManager() - { - return _applicationContext.Container.Resolve(); - } - - #endregion - - #region server & service objects - - public IMiningServer GetMiningServer(string type, IPoolConfig poolConfig, IPool pool, IMinerManager minerManager, IJobManager jobManager, - IBanManager banManager) + public IMiningServer GetMiningServer(string type, IPoolConfig poolConfig, IPool pool, IMinerManager minerManager, IJobManager jobManager, IBanManager banManager) { var @params = new NamedParameterOverloads { @@ -269,14 +261,23 @@ public IRpcService GetMiningService(string type, IPoolConfig poolConfig, IShareM return _applicationContext.Container.Resolve(type, @params); } - public IWebServer GetWebServer() + #endregion + + #region hash algorithms + + /// + /// Returns instance of the given hash algorithm. + /// + /// + /// + public IHashAlgorithm GetHashAlgorithm(string algorithm) { - return _applicationContext.Container.Resolve(); + return _applicationContext.Container.Resolve(algorithm); } - public INancyBootstrapper GetWebBootstrapper() + public IAlgorithmManager GetAlgorithmManager(IPoolManager poolManager) { - return _applicationContext.Container.Resolve(); + return _applicationContext.Container.Resolve(); } #endregion @@ -321,11 +322,16 @@ public IMigrationManager GetMigrationManager(IMySqlProvider provider, IPoolConfi #endregion - #region other objects + #region web-server objects - public ILogManager GetLogManager() + public IWebServer GetWebServer() { - return _applicationContext.Container.Resolve(); + return _applicationContext.Container.Resolve(); + } + + public INancyBootstrapper GetWebBootstrapper() + { + return _applicationContext.Container.Resolve(); } public IMetricsManager GetMetricsManager() diff --git a/src/CoiniumServ/Jobs/IJob.cs b/src/CoiniumServ/Jobs/IJob.cs index cb039b3a0..7e2f6344c 100644 --- a/src/CoiniumServ/Jobs/IJob.cs +++ b/src/CoiniumServ/Jobs/IJob.cs @@ -28,41 +28,94 @@ using CoiniumServ.Shares; using CoiniumServ.Transactions; using CoiniumServ.Utils.Numerics; +using Newtonsoft.Json; namespace CoiniumServ.Jobs { + + [JsonArray] public interface IJob : IEnumerable { + /// + /// ID of the job. Use this ID while submitting share generated from this job. + /// UInt64 Id { get; } + /// + /// Height of the block we are looking for. + /// int Height { get; } + /// + /// Hash of previous block. + /// string PreviousBlockHash { get; } string PreviousBlockHashReversed { get; } + /// + /// Initial part of coinbase transaction. + /// The miner inserts ExtraNonce1 and ExtraNonce2 after this section of the coinbase. (https://www.btcguild.com/new_protocol.php) + /// string CoinbaseInitial { get; } + /// + /// Final part of coinbase transaction. + /// The miner appends this after the first part of the coinbase and the two ExtraNonce values. (https://www.btcguild.com/new_protocol.php) + /// string CoinbaseFinal { get; } + /// + /// Coin's block version. + /// string Version { get; } + /// + /// Encoded current network difficulty. + /// string EncodedDifficulty { get; } BigInteger Target { get; } + /// + /// Job difficulty. + /// double Difficulty { get; } - string nTime { get; } + /// + /// The current time. nTime rolling should be supported, but should not increase faster than actual time. + /// + string NTime { get; } + /// + /// When true, server indicates that submitting shares from previous jobs don't have a sense and such shares will be rejected. When this flag is set, miner should also drop all previous jobs, so job_ids can be eventually rotated. (http://mining.bitcoin.cz/stratum-mining) + /// f true, miners should abort their current work and immediately use the new job. If false, they can still use the current job, but should move to the new one after exhausting the current nonce range. (https://www.btcguild.com/new_protocol.php) + /// bool CleanJobs { get; set; } + /// + /// Creation time of the job. + /// + int CreationTime { get; } + + /// + /// The assigned hash algorithm for the job. + /// IHashAlgorithm HashAlgorithm { get; } + /// + /// Associated block template. + /// IBlockTemplate BlockTemplate { get; } + /// + /// Associated generation transaction. + /// IGenerationTransaction GenerationTransaction { get; } + /// + /// Merkle tree associated to blockTemplate transactions. + /// IMerkleTree MerkleTree { get; } new IEnumerator GetEnumerator(); diff --git a/src/CoiniumServ/Jobs/Job.cs b/src/CoiniumServ/Jobs/Job.cs index 42268ae37..2beec8ea0 100644 --- a/src/CoiniumServ/Jobs/Job.cs +++ b/src/CoiniumServ/Jobs/Job.cs @@ -32,98 +32,46 @@ using CoiniumServ.Transactions; using CoiniumServ.Transactions.Utils; using CoiniumServ.Utils.Extensions; +using CoiniumServ.Utils.Helpers.Time; using CoiniumServ.Utils.Numerics; using Gibbed.IO; -using Newtonsoft.Json; namespace CoiniumServ.Jobs { - [JsonArray] public class Job : IJob { - /// - /// ID of the job. Use this ID while submitting share generated from this job. - /// - [JsonIgnore] public UInt64 Id { get; private set; } - /// - /// Height of the block we are looking for. - /// public int Height { get; private set; } public string PreviousBlockHash { get; private set; } - /// - /// Hash of previous block. - /// - [JsonIgnore] public string PreviousBlockHashReversed { get; private set; } - /// - /// Initial part of coinbase transaction. - /// The miner inserts ExtraNonce1 and ExtraNonce2 after this section of the coinbase. (https://www.btcguild.com/new_protocol.php) - /// - [JsonIgnore] public string CoinbaseInitial { get; private set; } - /// - /// Final part of coinbase transaction. - /// The miner appends this after the first part of the coinbase and the two ExtraNonce values. (https://www.btcguild.com/new_protocol.php) - /// - [JsonIgnore] public string CoinbaseFinal { get; private set; } - /// - /// Coin's block version. - /// - [JsonIgnore] public string Version { get; private set; } - /// - /// Encoded current network difficulty. - /// - [JsonIgnore] public string EncodedDifficulty { get; private set; } public BigInteger Target { get; private set; } - /// - /// Job difficulty. - /// public double Difficulty { get; private set; } - /// - /// The current time. nTime rolling should be supported, but should not increase faster than actual time. - /// - [JsonIgnore] - public string nTime { get; private set; } + public string NTime { get; private set; } - /// - /// When true, server indicates that submitting shares from previous jobs don't have a sense and such shares will be rejected. When this flag is set, miner should also drop all previous jobs, so job_ids can be eventually rotated. (http://mining.bitcoin.cz/stratum-mining) - /// f true, miners should abort their current work and immediately use the new job. If false, they can still use the current job, but should move to the new one after exhausting the current nonce range. (https://www.btcguild.com/new_protocol.php) - /// - [JsonIgnore] public bool CleanJobs { get; set; } - /// - /// The assigned hash algorithm for the job. - /// + public int CreationTime { get; private set; } + public IHashAlgorithm HashAlgorithm { get; private set; } - /// - /// Associated block template. - /// public IBlockTemplate BlockTemplate { get; private set; } - /// - /// Associated generation transaction. - /// public IGenerationTransaction GenerationTransaction { get; private set; } - /// - /// Merkle tree associated to blockTemplate transactions. - /// public IMerkleTree MerkleTree { get; private set; } /// @@ -146,12 +94,13 @@ public Job(UInt64 id, IHashAlgorithm algorithm, IBlockTemplate blockTemplate, IG BlockTemplate = blockTemplate; Height = blockTemplate.Height; GenerationTransaction = generationTransaction; - _shares = new List(); - 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(); // calculate the merkle tree MerkleTree = new MerkleTree(BlockTemplate.Transactions.GetHashList()); @@ -171,7 +120,7 @@ public Job(UInt64 id, IHashAlgorithm algorithm, IBlockTemplate blockTemplate, IG Difficulty = ((double)new BigRational(AlgorithmManager.Diff1, Target)); // set the ntime - nTime = BitConverter.GetBytes(blockTemplate.CurTime.BigEndian()).ToHexString(); + NTime = BitConverter.GetBytes(blockTemplate.CurTime.BigEndian()).ToHexString(); } public IEnumerator GetEnumerator() @@ -185,7 +134,7 @@ public IEnumerator GetEnumerator() MerkleTree.Branches, Version, EncodedDifficulty, - nTime, + NTime, CleanJobs }; diff --git a/src/CoiniumServ/Jobs/Manager/JobManager.cs b/src/CoiniumServ/Jobs/Manager/JobManager.cs index 56004a00e..0925dbeb7 100644 --- a/src/CoiniumServ/Jobs/Manager/JobManager.cs +++ b/src/CoiniumServ/Jobs/Manager/JobManager.cs @@ -52,8 +52,6 @@ public class JobManager : IJobManager private readonly IPoolConfig _poolConfig; - private readonly IJobConfig _jobConfig; - private IExtraNonce _extraNonce; // todo: check this. private readonly ILogger _logger; @@ -72,9 +70,7 @@ public JobManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJobTracke _shareManager = shareManager; _minerManager = minerManager; _hashAlgorithm = hashAlgorithm; - _poolConfig = poolConfig; - _jobConfig = poolConfig.Job; _jobCounter = new JobCounter(); // todo make this ioc based too. @@ -123,7 +119,7 @@ private void BlockPoller(object stats) } catch (RpcException) { } // just skip any exceptions caused by the block-pooler queries. - _blockPollerTimer.Change(_jobConfig.BlockRefreshInterval, Timeout.Infinite); // reset the block-poller timer so we can keep polling. + _blockPollerTimer.Change(_poolConfig.Job.BlockRefreshInterval, Timeout.Infinite); // reset the block-poller timer so we can keep polling. } private void CreateAndBroadcastNewJob(bool initiatedByTimer) @@ -134,16 +130,16 @@ private void CreateAndBroadcastNewJob(bool initiatedByTimer) { var count = BroadcastJob(job); // broadcast to miners. - _blockPollerTimer.Change(_jobConfig.BlockRefreshInterval, Timeout.Infinite); // reset the block-poller timer so we can start or keep polling for a new block in the network. + _blockPollerTimer.Change(_poolConfig.Job.BlockRefreshInterval, Timeout.Infinite); // reset the block-poller timer so we can start or keep polling for a new block in the network. if (initiatedByTimer) - _logger.Information("Broadcasted new job 0x{0:x} to {1} subscribers as no new blocks found for last {2} seconds", job.Id, count, _jobConfig.RebroadcastTimeout); + _logger.Information("Broadcasted new job 0x{0:x} to {1} subscribers as no new blocks found for last {2} seconds", job.Id, count, _poolConfig.Job.RebroadcastTimeout); else _logger.Information("Broadcasted new job 0x{0:x} to {1} subscribers as network found a new block {2}", job.Id, count, job.Height); } // no matter we created a job successfully or not, reset the rebroadcast timer, so we can keep trying. - _reBroadcastTimer.Change(_jobConfig.RebroadcastTimeout * 1000, Timeout.Infinite); + _reBroadcastTimer.Change(_poolConfig.Job.RebroadcastTimeout * 1000, Timeout.Infinite); } private IJob GetNewJob() diff --git a/src/CoiniumServ/Jobs/Tracker/JobTracker.cs b/src/CoiniumServ/Jobs/Tracker/JobTracker.cs index a3a5e8a6b..7c8f15a2c 100644 --- a/src/CoiniumServ/Jobs/Tracker/JobTracker.cs +++ b/src/CoiniumServ/Jobs/Tracker/JobTracker.cs @@ -22,18 +22,35 @@ #endregion using System; using System.Collections.Generic; +using System.Linq; +using System.Threading; +using CoiniumServ.Pools; +using CoiniumServ.Utils.Helpers.Time; +using Serilog; namespace CoiniumServ.Jobs.Tracker { public class JobTracker:IJobTracker { - private readonly Dictionary _jobs; + private Dictionary _jobs; + + private readonly Timer _cleanupTimer; // timer for cleaning old jobs. + + private readonly ILogger _logger; public IJob Current { get; private set; } - public JobTracker() + private const int MinimumJobBacklog = 3; // number of jobs to leave in. + private readonly int _cleanupFrequency; // frequency to cleanup jobs in seconds. + + public JobTracker(IPoolConfig poolConfig) { _jobs = new Dictionary(); + _logger = Log.ForContext().ForContext("Component", poolConfig.Coin.Name); + + _cleanupFrequency = MinimumJobBacklog*poolConfig.Job.RebroadcastTimeout; // calculate the cleanup frequency = number of jobs in backlog * rebroad-timeout + _cleanupTimer = new Timer(CleanUp, null, Timeout.Infinite, Timeout.Infinite); // create the timer as disabled. + _cleanupTimer.Change(_cleanupFrequency * 1000, Timeout.Infinite); // adjust the timer's next run. } public IJob Get(UInt64 id) @@ -47,6 +64,23 @@ public void Add(IJob job) Current = job; } - // TODO: remove expired jobs. + 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. + } } } diff --git a/src/CoiniumServ/Miners/IMiner.cs b/src/CoiniumServ/Miners/IMiner.cs index 757e534d8..1610a19be 100644 --- a/src/CoiniumServ/Miners/IMiner.cs +++ b/src/CoiniumServ/Miners/IMiner.cs @@ -35,11 +35,13 @@ public interface IMiner /// /// Unique subscription id for identifying the miner. /// + [JsonProperty("id")] int Id { get; } /// /// Username of the miner. /// + [JsonProperty("username")] string Username { get; } /// @@ -50,6 +52,7 @@ public interface IMiner /// /// Is the miner authenticated. /// + [JsonProperty("authenticated")] bool Authenticated { get; set; } int ValidShares { get; set; } diff --git a/src/CoiniumServ/Persistance/Layers/Empty/EmptyStorageLayer.cs b/src/CoiniumServ/Persistance/Layers/Empty/EmptyStorageLayer.cs index 2616dcb90..6084ec723 100644 --- a/src/CoiniumServ/Persistance/Layers/Empty/EmptyStorageLayer.cs +++ b/src/CoiniumServ/Persistance/Layers/Empty/EmptyStorageLayer.cs @@ -32,9 +32,6 @@ namespace CoiniumServ.Persistance.Layers.Empty public class EmptyStorageLayer : IStorageLayer { public bool IsEnabled { get; private set; } - public bool SupportsShareStorage { get { return false; } } - public bool SupportsBlockStorage { get { return false; } } - public bool SupportsPaymentsStorage { get { return false; } } public void AddShare(IShare share) { @@ -66,6 +63,16 @@ public Dictionary> GetShares(IList>(); // return an empty dictionary. } + public void DeleteExpiredHashrateData(int until) + { + return; // just skip. + } + + public IDictionary GetHashrateData(int since) + { + return new Dictionary(); // return an empty dictionary. + } + public void AddBlock(IShare share) { return; // just skip. diff --git a/src/CoiniumServ/Persistance/Layers/Hybrid/HybridStorageLayer.cs b/src/CoiniumServ/Persistance/Layers/Hybrid/HybridStorageLayer.cs index e8b3e2ab9..109e5d1e4 100644 --- a/src/CoiniumServ/Persistance/Layers/Hybrid/HybridStorageLayer.cs +++ b/src/CoiniumServ/Persistance/Layers/Hybrid/HybridStorageLayer.cs @@ -46,9 +46,6 @@ namespace CoiniumServ.Persistance.Layers.Hybrid public class HybridStorageLayer : IStorageLayer { public bool IsEnabled { get; private set; } - public bool SupportsShareStorage { get { return true; } } - public bool SupportsBlockStorage { get { return true; } } - public bool SupportsPaymentsStorage { get { return true; } } private readonly IDaemonClient _daemonClient; @@ -238,6 +235,55 @@ public Dictionary> GetShares(IList GetHashrateData(int since) + { + var hashrates = new Dictionary(); + + try + { + if (!IsEnabled || !_redisProvider.IsConnected) + return hashrates; + + var key = string.Format("{0}:hashrate", _coin); + + var results = _redisProvider.Client.ZRangeByScore(key, since, double.PositiveInfinity); + + foreach (var result in results) + { + var data = result.Split(':'); + var share = double.Parse(data[0].Replace(',', '.'), CultureInfo.InvariantCulture); + var worker = data[1]; + + if (!hashrates.ContainsKey(worker)) + hashrates.Add(worker, 0); + + hashrates[worker] += share; + } + } + catch (Exception e) + { + _logger.Error("An exception occured while getting hashrate data: {0:l}", e.Message); + } + + return hashrates; + } + public void AddBlock(IShare share) { try @@ -280,7 +326,7 @@ public void UpdateBlock(IPaymentRound round) { orphaned = round.Block.Status == BlockStatus.Orphaned, confirmed = round.Block.Status == BlockStatus.Confirmed, - height = round.Block.Status + height = round.Block.Height }); } } diff --git a/src/CoiniumServ/Persistance/Layers/Hybrid/MigrationManager.cs b/src/CoiniumServ/Persistance/Layers/Hybrid/MigrationManager.cs index c3d1984e5..4bb28cf83 100644 --- a/src/CoiniumServ/Persistance/Layers/Hybrid/MigrationManager.cs +++ b/src/CoiniumServ/Persistance/Layers/Hybrid/MigrationManager.cs @@ -28,6 +28,7 @@ using FluentMigrator.Runner; using FluentMigrator.Runner.Announcers; using FluentMigrator.Runner.Initialization; +using MySql.Data.MySqlClient; using Serilog; namespace CoiniumServ.Persistance.Layers.Hybrid @@ -48,16 +49,23 @@ public MigrationManager(IMySqlProvider provider, IPoolConfig poolConfig) private void Check() { - var announcer = new TextWriterAnnouncer(WriteLog); - var assembly = Assembly.GetExecutingAssembly(); - var migrationContext = new RunnerContext(announcer); + try + { + var announcer = new TextWriterAnnouncer(WriteLog); + var assembly = Assembly.GetExecutingAssembly(); + var migrationContext = new RunnerContext(announcer); - var options = new MigrationOptions { PreviewOnly = false, Timeout = 60 }; - var factory = new FluentMigrator.Runner.Processors.MySql.MySqlProcessorFactory(); - var processor = factory.Create(_provider.ConnectionString, announcer, options); - var runner = new MigrationRunner(assembly, migrationContext, processor); + var options = new MigrationOptions {PreviewOnly = false, Timeout = 60}; + var factory = new FluentMigrator.Runner.Processors.MySql.MySqlProcessorFactory(); + var processor = factory.Create(_provider.ConnectionString, announcer, options); + var runner = new MigrationRunner(assembly, migrationContext, processor); - runner.MigrateUp(true); + runner.MigrateUp(true); + } + catch (MySqlException e) + { + _logger.Error("An exception occured while running migration manager. Please ensure your mysql settings are correct; {0:l}", e.Message); + } } private void WriteLog(string s) diff --git a/src/CoiniumServ/Persistance/Layers/IStorageLayer.cs b/src/CoiniumServ/Persistance/Layers/IStorageLayer.cs index cc71736e5..c7762f8ab 100644 --- a/src/CoiniumServ/Persistance/Layers/IStorageLayer.cs +++ b/src/CoiniumServ/Persistance/Layers/IStorageLayer.cs @@ -37,8 +37,6 @@ public interface IStorageLayer #region share storage - bool SupportsShareStorage { get; } - void AddShare(IShare share); void RemoveShares(IPaymentRound round); @@ -51,12 +49,14 @@ public interface IStorageLayer Dictionary> GetShares(IList rounds); + void DeleteExpiredHashrateData(int until); + + IDictionary GetHashrateData(int since); + #endregion #region block storage - bool SupportsBlockStorage { get; } - void AddBlock(IShare share); void UpdateBlock(IPaymentRound round); @@ -78,8 +78,6 @@ public interface IStorageLayer #region payments storage - bool SupportsPaymentsStorage { get; } - Dictionary GetPreviousBalances(); void SetBalances(IList workerBalances); diff --git a/src/CoiniumServ/Persistance/Layers/Mpos/MposStorageLayer.cs b/src/CoiniumServ/Persistance/Layers/Mpos/MposStorageLayer.cs index 1055c5170..55a2d0f01 100644 --- a/src/CoiniumServ/Persistance/Layers/Mpos/MposStorageLayer.cs +++ b/src/CoiniumServ/Persistance/Layers/Mpos/MposStorageLayer.cs @@ -42,9 +42,6 @@ namespace CoiniumServ.Persistance.Layers.Mpos public class MposStorageLayer : IStorageLayer { public bool IsEnabled { get; private set; } - public bool SupportsShareStorage { get { return true; } } - public bool SupportsBlockStorage { get { return true; } } - public bool SupportsPaymentsStorage { get { return true; } } private readonly IMySqlProvider _mySqlProvider; @@ -59,6 +56,8 @@ public MposStorageLayer(IEnumerable providers, PoolConfig pool if (provider is IMySqlProvider) _mySqlProvider = (IMySqlProvider) provider; } + + IsEnabled = _mySqlProvider != null; } public void AddShare(IShare share) @@ -152,6 +151,38 @@ public Dictionary> GetShares(IList GetHashrateData(int since) + { + var hashrateData = new Dictionary(); + + try + { + if (!IsEnabled) + return hashrateData; + + using (var connection = new MySqlConnection(_mySqlProvider.ConnectionString)) + { + var results = connection.Query(@"select username, sum(difficulty) as shares from shares where our_result='Y' group by username"); + + foreach (var row in results) + { + hashrateData.Add(row.username, row.shares); + } + } + } + catch (Exception e) + { + _logger.Error("An exception occured while getting share data: {0:l}", e.Message); + } + + return hashrateData; + } + public void AddBlock(IShare share) { // Blocks are handled by MPOS by itself, we don't need to persist found block data. @@ -276,6 +307,9 @@ public bool Authenticate(IMiner miner) { try { + if (!IsEnabled) + return false; + using (var connection = new MySqlConnection(_mySqlProvider.ConnectionString)) { // query the username against mpos pool_worker table. @@ -299,6 +333,9 @@ public void UpdateDifficulty(IStratumMiner miner) { try { + if (!IsEnabled) + return; + using (var connection = new MySqlConnection(_mySqlProvider.ConnectionString)) { connection.Execute( diff --git a/src/CoiniumServ/Statistics/INetworkStats.cs b/src/CoiniumServ/Pools/INetworkInfo.cs similarity index 50% rename from src/CoiniumServ/Statistics/INetworkStats.cs rename to src/CoiniumServ/Pools/INetworkInfo.cs index 2fc856c3b..bd50fab5a 100644 --- a/src/CoiniumServ/Statistics/INetworkStats.cs +++ b/src/CoiniumServ/Pools/INetworkInfo.cs @@ -20,22 +20,74 @@ // license or white-label it as set out in licenses/commercial.txt. // #endregion + using System; using CoiniumServ.Server.Web.Service; using Newtonsoft.Json; -namespace CoiniumServ.Statistics +namespace CoiniumServ.Pools { [JsonObject(MemberSerialization.OptIn)] - public interface INetworkStats: IJsonService + public interface INetworkInfo: IJsonService { + /// + /// Network difficulty + /// [JsonProperty("difficulty")] double Difficulty { get; } + /// + /// Current round. + /// [JsonProperty("round")] int Round { get; } + /// + /// Network hashrate + /// [JsonProperty("hashrate")] UInt64 Hashrate { get; } + + /// + /// Coin version. + /// + [JsonProperty("version")] + string CoinVersion { get; } + + /// + /// Protocol version + /// + [JsonProperty("protocol")] + int ProtocolVersion { get; } + + /// + /// Wallet version + /// + [JsonProperty("wallet")] + int WalletVersion { get; } + + /// + /// Is testnet? + /// + [JsonProperty("testnet")] + bool Testnet { get; } + + /// + /// Count of connected peers. + /// + [JsonProperty("connections")] + long Connections { get; } + + /// + /// Any errors reported by coin network + /// + [JsonProperty("errors")] + string Errors { get; } + + /// + /// Is the network connection healthy? + /// + [JsonProperty("healthy")] + bool Healthy { get; } } } diff --git a/src/CoiniumServ/Pools/IPool.cs b/src/CoiniumServ/Pools/IPool.cs index febeb7b93..de186724f 100644 --- a/src/CoiniumServ/Pools/IPool.cs +++ b/src/CoiniumServ/Pools/IPool.cs @@ -25,7 +25,6 @@ using CoiniumServ.Cryptology.Algorithms; using CoiniumServ.Miners; using CoiniumServ.Server.Web.Service; -using CoiniumServ.Statistics; using Newtonsoft.Json; namespace CoiniumServ.Pools @@ -33,22 +32,22 @@ namespace CoiniumServ.Pools [JsonObject(MemberSerialization.OptIn)] public interface IPool: IJsonService { - [JsonProperty("config")] - IPoolConfig Config { get; } - [JsonProperty("hashrate")] UInt64 Hashrate { get; } [JsonProperty("round")] Dictionary RoundShares { get; } + [JsonProperty("config")] + IPoolConfig Config { get; } + IHashAlgorithm HashAlgorithm { get; } [JsonProperty("miners")] IMinerManager MinerManager { get; } [JsonProperty("network")] - INetworkStats NetworkStats { get; } + INetworkInfo NetworkInfo { get; } [JsonProperty("blocks")] IBlocksCache BlocksCache { get; } diff --git a/src/CoiniumServ/Pools/NetworkInfo.cs b/src/CoiniumServ/Pools/NetworkInfo.cs new file mode 100644 index 000000000..7806e70ad --- /dev/null +++ b/src/CoiniumServ/Pools/NetworkInfo.cs @@ -0,0 +1,152 @@ +#region License +// +// CoiniumServ - Crypto Currency Mining Pool Server Software +// Copyright (C) 2013 - 2014, CoiniumServ Project - http://www.coinium.org +// http://www.coiniumserv.com - https://github.com/CoiniumServ/CoiniumServ +// +// This software is dual-licensed: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// For the terms of this license, see licenses/gpl_v3.txt. +// +// Alternatively, you can license this software under a commercial +// license or white-label it as set out in licenses/commercial.txt. +// +#endregion + +using CoiniumServ.Coin.Helpers; +using CoiniumServ.Cryptology.Algorithms; +using CoiniumServ.Daemon; +using CoiniumServ.Daemon.Exceptions; +using Serilog; + +namespace CoiniumServ.Pools +{ + public class NetworkInfo:INetworkInfo + { + public double Difficulty { get; private set; } + public int Round { get; private set; } + public ulong Hashrate { get; private set; } + public string CoinVersion { get; private set; } + public int ProtocolVersion { get; private set; } + public int WalletVersion { get; private set; } + public bool Testnet { get; private set; } + public long Connections { get; private set; } + public string Errors { get; private set; } + public bool Healthy { get; private set; } + public string ServiceResponse { get; private set; } // todo implement this too for /pool/COIN/network + + private readonly IDaemonClient _daemonClient; + + private readonly IHashAlgorithm _hashAlgorithm; + + private readonly IPoolConfig _poolConfig; + + private readonly ILogger _logger; + + // todo: add %51 hash power detection support. + + public NetworkInfo(IDaemonClient daemonClient, IHashAlgorithm hashAlgorithm, IPoolConfig poolConfig) + { + _daemonClient = daemonClient; + _hashAlgorithm = hashAlgorithm; + _poolConfig = poolConfig; + _logger = Log.ForContext().ForContext("Component", poolConfig.Coin.Name); + + Recache(); // recache the data initially. + PrintNetworkInfo(); + DetectProofOfStakeCoin(); // detect if we are running on a proof-of-stake coin. + } + + public void Recache() + { + try // read getinfo() based data. + { + var info = _daemonClient.GetInfo(); + + // read data. + CoinVersion = info.Version; + ProtocolVersion = info.ProtocolVersion; + WalletVersion = info.WalletVersion; + Testnet = info.Testnet; + Connections = info.Connections; + Errors = info.Errors; + + // check if our network connection is healthy. + Healthy = Connections >= 0 && string.IsNullOrEmpty(Errors); + } + catch (RpcException e) + { + _logger.Error("Can not read getinfo(): {0:l}", e.Message); + Healthy = false; // set healthy status to false as we couldn't get a reply. + } + + try // read mininginfo() based data. + { + var miningInfo = _daemonClient.GetMiningInfo(); + + // read data. + Hashrate = miningInfo.NetworkHashps; + Difficulty = miningInfo.Difficulty; + Round = miningInfo.Blocks + 1; + } + catch (RpcException e) + { + _logger.Error("Can not read mininginfo() - the coin may not support the request: {0:l}", e.Message); + Hashrate = 0; + Difficulty = 0; + Round = -1; + Healthy = false; // set healthy status to false as we couldn't get a reply. + } + } + + private void PrintNetworkInfo() + { + _logger.Information("symbol: {0:l} algorithm: {1:l} " + + "version: {2:l} protocol: {3} wallet: {4} " + + "network difficulty: {5:0.00000000} block difficulty: {6:0.00} network hashrate: {7:l} " + + "network: {8:l} peers: {9} blocks: {10} errors: {11:l} ", + _poolConfig.Coin.Symbol, + _poolConfig.Coin.Algorithm, + CoinVersion, + ProtocolVersion, + WalletVersion, + Difficulty, + Difficulty*_hashAlgorithm.Multiplier, + Hashrate.GetReadableHashrate(), + Testnet ? "testnet" : "mainnet", + Connections, + Round - 1, + string.IsNullOrEmpty(Errors) ? "none" : Errors); + } + + private void DetectProofOfStakeCoin() + { + // use getdifficulty() to determine if it's POS coin. + + try + { + /* By default proof-of-work coins return a floating point as difficulty (https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_calls_lis). + * Though proof-of-stake coins returns a json-object; + * { "proof-of-work" : 41867.16992903, "proof-of-stake" : 0.00390625, "search-interval" : 0 } + * So basically we can use this info to determine if assigned coin is a proof-of-stake one. + */ + + var response = _daemonClient.MakeRawRequest("getdifficulty"); + if (response.Contains("proof-of-stake")) // if response contains proof-of-stake field + _poolConfig.Coin.IsPOS = true; // then automatically set coin-config.IsPOS to true. + } + catch (RpcException e) + { + _logger.Error("Can not read getdifficulty() - the coin may not support the request: {0:l}", e.Message); + } + } + } +} diff --git a/src/CoiniumServ/Pools/NetworkStats.cs b/src/CoiniumServ/Pools/NetworkStats.cs deleted file mode 100644 index e90cf93db..000000000 --- a/src/CoiniumServ/Pools/NetworkStats.cs +++ /dev/null @@ -1,61 +0,0 @@ -#region License -// -// CoiniumServ - Crypto Currency Mining Pool Server Software -// Copyright (C) 2013 - 2014, CoiniumServ Project - http://www.coinium.org -// http://www.coiniumserv.com - https://github.com/CoiniumServ/CoiniumServ -// -// This software is dual-licensed: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// For the terms of this license, see licenses/gpl_v3.txt. -// -// Alternatively, you can license this software under a commercial -// license or white-label it as set out in licenses/commercial.txt. -// -#endregion -using CoiniumServ.Daemon; -using CoiniumServ.Daemon.Exceptions; -using CoiniumServ.Statistics; - -namespace CoiniumServ.Pools -{ - public class NetworkStats:INetworkStats - { - public double Difficulty { get; private set; } - public int Round { get; private set; } - public ulong Hashrate { get; private set; } - - private readonly IDaemonClient _daemonClient; - - public NetworkStats(IDaemonClient daemonClient) - { - _daemonClient = daemonClient; - } - - public string ServiceResponse { get; private set; } - - public void Recache() - { - try - { - var miningInfo = _daemonClient.GetMiningInfo(); - Hashrate = miningInfo.NetworkHashps; - Difficulty = miningInfo.Difficulty; - Round = miningInfo.Blocks + 1; - } - catch (RpcException) - { - Hashrate = 0; - Difficulty = 0; - Round = -1; - } - } - } -} diff --git a/src/CoiniumServ/Pools/Pool.cs b/src/CoiniumServ/Pools/Pool.cs index 1a808deb4..cf2182302 100644 --- a/src/CoiniumServ/Pools/Pool.cs +++ b/src/CoiniumServ/Pools/Pool.cs @@ -25,10 +25,9 @@ using System.Linq; using System.Security.Cryptography; using CoiniumServ.Banning; -using CoiniumServ.Coin.Helpers; +using CoiniumServ.Configuration; using CoiniumServ.Cryptology.Algorithms; using CoiniumServ.Daemon; -using CoiniumServ.Daemon.Exceptions; using CoiniumServ.Factories; using CoiniumServ.Jobs.Manager; using CoiniumServ.Miners; @@ -41,7 +40,7 @@ using CoiniumServ.Server.Mining; using CoiniumServ.Server.Mining.Service; using CoiniumServ.Shares; -using CoiniumServ.Statistics; +using CoiniumServ.Utils.Helpers.Time; using CoiniumServ.Utils.Helpers.Validation; using Newtonsoft.Json; using Serilog; @@ -53,12 +52,17 @@ namespace CoiniumServ.Pools /// public class Pool : IPool { - public IPoolConfig Config { get; private set; } public ulong Hashrate { get; private set; } public Dictionary RoundShares { get; private set; } + + public IPoolConfig Config { get; private set; } + public IHashAlgorithm HashAlgorithm { get; private set; } + public IMinerManager MinerManager { get; private set; } - public INetworkStats NetworkStats { get; private set; } + + public INetworkInfo NetworkInfo { get; private set; } + public IBlocksCache BlocksCache { get; private set; } // object factory. @@ -75,8 +79,12 @@ public class Pool : IPool private IStorageLayer _storageLayer; + private readonly IConfigManager _configManager; + private Dictionary _servers; + private double _shareMultiplier; // share multiplier to be used in hashrate calculation. + private readonly ILogger _logger; /// @@ -88,12 +96,15 @@ public class Pool : IPool /// Initializes a new instance of the class. /// /// + /// /// - public Pool(IPoolConfig poolConfig, IObjectFactory objectFactory) + public Pool(IPoolConfig poolConfig, IConfigManager configManager, IObjectFactory objectFactory) { - Enforce.ArgumentNotNull(() => poolConfig); // make sure we have a config instance supplied. + Enforce.ArgumentNotNull(() => poolConfig); // make sure we have a pool-config instance supplied. + Enforce.ArgumentNotNull(() => configManager); // make sure we have a config-manager instance supplied. Enforce.ArgumentNotNull(() => objectFactory); // make sure we have a objectFactory instance supplied. + _configManager = configManager; _objectFactory = objectFactory; // TODO: validate pool central wallet & rewards within the startup. @@ -120,63 +131,9 @@ private void InitDaemon() _daemonClient = _objectFactory.GetDaemonClient(Config); HashAlgorithm = _objectFactory.GetHashAlgorithm(Config.Coin.Algorithm); - - // read getinfo(). - try - { - var info = _daemonClient.GetInfo(); - - _logger.Information("Coin symbol: {0:l} algorithm: {1:l} " + - "Coin version: {2:l} protocol: {3} wallet: {4} " + - "Daemon network: {5:l} peers: {6} blocks: {7} errors: {8:l} ", - Config.Coin.Symbol, - Config.Coin.Algorithm, - info.Version, - info.ProtocolVersion, - info.WalletVersion, - info.Testnet ? "testnet" : "mainnet", - info.Connections, info.Blocks, - string.IsNullOrEmpty(info.Errors) ? "none" : info.Errors); - } - catch (RpcException e) - { - _logger.Error("Can not read getinfo(): {0:l}", e.Message); - return; - } - - // read getmininginfo(). - try - { - // try reading mininginfo(), some coins may not support it. - var miningInfo = _daemonClient.GetMiningInfo(); - - _logger.Information("Network difficulty: {0:0.00000000} block difficulty: {1:0.00} Network hashrate: {2:l} ", - miningInfo.Difficulty, - miningInfo.Difficulty * HashAlgorithm.Multiplier, - miningInfo.NetworkHashps.GetReadableHashrate()); - } - catch (RpcException e) - { - _logger.Error("Can not read mininginfo() - the coin may not support the request: {0:l}", e.Message); - } - - // read getdifficulty() to determine if it's POS coin. - try - { - /* By default proof-of-work coins return a floating point as difficulty (https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_calls_lis). - * Though proof-of-stake coins returns a json-object; - * { "proof-of-work" : 41867.16992903, "proof-of-stake" : 0.00390625, "search-interval" : 0 } - * So basically we can use this info to determine if assigned coin is a proof-of-stake one. - */ - - var response = _daemonClient.MakeRawRequest("getdifficulty"); - if (response.Contains("proof-of-stake")) // if response contains proof-of-stake field - Config.Coin.IsPOS = true; // then automatically set coin-config.IsPOS to true. - } - catch (RpcException e) - { - _logger.Error("Can not read getdifficulty() - the coin may not support the request: {0:l}", e.Message); - } + NetworkInfo = _objectFactory.GetNetworkInfo(_daemonClient, HashAlgorithm, Config); + + _shareMultiplier = Math.Pow(2, 32) / HashAlgorithm.Multiplier; // will be used in hashrate calculation. } private void InitStorage() @@ -208,9 +165,8 @@ private void InitManagers() { BlocksCache = _objectFactory.GetBlocksCache(_storageLayer); MinerManager = _objectFactory.GetMinerManager(Config, _storageLayer); - NetworkStats = _objectFactory.GetNetworkStats(_daemonClient); - var jobTracker = _objectFactory.GetJobTracker(); + var jobTracker = _objectFactory.GetJobTracker(Config); var blockProcessor = _objectFactory.GetBlockProcessor(Config, _daemonClient); _shareManager = _objectFactory.GetShareManager(Config, _daemonClient, jobTracker, _storageLayer, blockProcessor); _objectFactory.GetVardiffManager(Config, _shareManager); @@ -289,7 +245,7 @@ private void GenerateInstanceId() public void Recache() { BlocksCache.Recache(); // recache the blocks. - NetworkStats.Recache(); // let network statistics recache. + NetworkInfo.Recache(); // let network statistics recache. CalculateHashrate(); // calculate the pool hashrate. RecacheRound(); // recache current round. @@ -304,13 +260,13 @@ private void RecacheRound() private void CalculateHashrate() { - //// read hashrate stats. - //var windowTime = TimeHelpers.NowInUnixTime() - _statisticsConfig.HashrateWindow; - //_storage.DeleteExpiredHashrateData(windowTime); - //var hashrates = _storage.GetHashrateData(windowTime); + // read hashrate stats. + var windowTime = TimeHelpers.NowInUnixTime() - _configManager.StatisticsConfig.HashrateWindow; + _storageLayer.DeleteExpiredHashrateData(windowTime); + var hashrates = _storageLayer.GetHashrateData(windowTime); - //double total = hashrates.Sum(pair => pair.Value); - //Hashrate = Convert.ToUInt64(_shareMultiplier * total / _statisticsConfig.HashrateWindow); + double total = hashrates.Sum(pair => pair.Value); + Hashrate = Convert.ToUInt64(_shareMultiplier * total / _configManager.StatisticsConfig.HashrateWindow); } } } diff --git a/src/CoiniumServ/Server/Mining/Stratum/IStratumMiner.cs b/src/CoiniumServ/Server/Mining/Stratum/IStratumMiner.cs index 217a5b92c..5c28181c6 100644 --- a/src/CoiniumServ/Server/Mining/Stratum/IStratumMiner.cs +++ b/src/CoiniumServ/Server/Mining/Stratum/IStratumMiner.cs @@ -24,9 +24,11 @@ using CoiniumServ.Jobs; using CoiniumServ.Miners; using CoiniumServ.Vardiff; +using Newtonsoft.Json; namespace CoiniumServ.Server.Mining.Stratum { + [JsonObject(MemberSerialization.OptIn)] public interface IStratumMiner:IMiner, IVardiffMiner { /// @@ -39,6 +41,7 @@ public interface IStratumMiner:IMiner, IVardiffMiner /// bool Subscribed { get; } + [JsonProperty("difficulty")] float Difficulty { get; } float PreviousDifficulty { get; } diff --git a/src/CoiniumServ/Server/Mining/Stratum/StratumMiner.cs b/src/CoiniumServ/Server/Mining/Stratum/StratumMiner.cs index 7fd6b1d87..8764a411e 100644 --- a/src/CoiniumServ/Server/Mining/Stratum/StratumMiner.cs +++ b/src/CoiniumServ/Server/Mining/Stratum/StratumMiner.cs @@ -54,13 +54,11 @@ public class StratumMiner : IClient, IStratumMiner /// /// Unique subscription id for identifying the miner. /// - [JsonProperty("id")] public int Id { get; private set; } /// /// Username of the miner. /// - [JsonProperty("username")] public string Username { get; private set; } /// @@ -71,7 +69,6 @@ public class StratumMiner : IClient, IStratumMiner /// /// Is the miner authenticated? /// - [JsonProperty("authenticated")] public bool Authenticated { get; set; } public int ValidShares { get; set; } @@ -79,9 +76,9 @@ public class StratumMiner : IClient, IStratumMiner public int InvalidShares { get; set; } public IPool Pool { get; private set; } - - [JsonProperty("difficulty")] + public float Difficulty { get; set; } + public float PreviousDifficulty { get; set; } /// @@ -90,7 +87,9 @@ public class StratumMiner : IClient, IStratumMiner public uint ExtraNonce { get; private set; } public int LastVardiffTimestamp { get; set; } + public int LastVardiffRetarget { get; set; } + public IRingBuffer VardiffBuffer { get; set; } private readonly IMinerManager _minerManager; diff --git a/src/CoiniumServ/Server/Web/IWebServerConfig.cs b/src/CoiniumServ/Server/Web/IWebServerConfig.cs index 1fb011dff..b8d64e458 100644 --- a/src/CoiniumServ/Server/Web/IWebServerConfig.cs +++ b/src/CoiniumServ/Server/Web/IWebServerConfig.cs @@ -20,9 +20,8 @@ // license or white-label it as set out in licenses/commercial.txt. // #endregion -using System; + using CoiniumServ.Configuration; -using CoiniumServ.Statistics; namespace CoiniumServ.Server.Web { @@ -40,8 +39,6 @@ public interface IWebServerConfig:IConfig /// int Port { get; } - IStatisticsConfig Statistics { get; } - IBackendConfig Backend { get; } } } diff --git a/src/CoiniumServ/Server/Web/Modules/Pool.cs b/src/CoiniumServ/Server/Web/Modules/Pool.cs index c0eb41142..a9a435cac 100644 --- a/src/CoiniumServ/Server/Web/Modules/Pool.cs +++ b/src/CoiniumServ/Server/Web/Modules/Pool.cs @@ -53,7 +53,7 @@ public PoolModule(IStatisticsManager statisticsManager, IPoolManager poolManager } ViewBag.Title = string.Format("{0} Pool", pool.Config.Coin.Name); - ViewBag.Heading = string.Format("{0} Pool Details", pool.Config.Coin.Name); + ViewBag.Heading = string.Format("{0} Pool", pool.Config.Coin.Name); // return our view return View["pool", new PoolModel diff --git a/src/CoiniumServ/Server/Web/WebServer.cs b/src/CoiniumServ/Server/Web/WebServer.cs index 877718f9e..bd0f79cf2 100644 --- a/src/CoiniumServ/Server/Web/WebServer.cs +++ b/src/CoiniumServ/Server/Web/WebServer.cs @@ -41,8 +41,6 @@ public WebServer(INancyBootstrapper webBootstrapper, IConfigManager configManage if (config.Enabled) Start(); - else - _logger.Verbose("Skipping web-server initialization as it disabled."); } } } diff --git a/src/CoiniumServ/Server/Web/WebServerConfig.cs b/src/CoiniumServ/Server/Web/WebServerConfig.cs index 5807dff14..ff974e947 100644 --- a/src/CoiniumServ/Server/Web/WebServerConfig.cs +++ b/src/CoiniumServ/Server/Web/WebServerConfig.cs @@ -32,7 +32,6 @@ public class WebServerConfig : IWebServerConfig public bool Enabled { get; private set; } public string BindInterface { get; private set; } public int Port { get; private set; } - public IStatisticsConfig Statistics { get; private set; } public IBackendConfig Backend { get; private set; } public bool Valid { get; private set; } public WebServerConfig(dynamic config) @@ -43,9 +42,7 @@ public WebServerConfig(dynamic config) Enabled = config.enabled; BindInterface = string.IsNullOrEmpty(config.bind) ? "127.0.0.1" : config.bind; Port = config.port == 0 ? 80 : config.port; - Statistics = new StatisticsConfig(config.stats); Backend = new BackendConfig(config.backend); - Valid = true; } catch (Exception e) diff --git a/src/CoiniumServ/Statistics/StatisticsManager.cs b/src/CoiniumServ/Statistics/StatisticsManager.cs index 919959f14..73cc2c0a6 100644 --- a/src/CoiniumServ/Statistics/StatisticsManager.cs +++ b/src/CoiniumServ/Statistics/StatisticsManager.cs @@ -34,25 +34,28 @@ namespace CoiniumServ.Statistics public class StatisticsManager:IStatisticsManager { public ulong Hashrate { get; private set; } + public int MinerCount { get; private set; } public DateTime LastUpdate { get; private set; } + public IAlgorithmManager Algorithms { get; private set; } + public IPoolManager Pools { get; private set; } + private readonly IStatisticsConfig _config; + private readonly Timer _recacheTimer; // timer for recaching stastics. private readonly Stopwatch _stopWatch = new Stopwatch(); - private readonly IStatisticsConfig _config; - private readonly ILogger _logger; public StatisticsManager(IConfigManager configManager, IPoolManager poolManager, IAlgorithmManager algorithmManager) { - _config = configManager.WebServerConfig.Statistics; Pools = poolManager; Algorithms = algorithmManager; + _config = configManager.StatisticsConfig; _logger = Log.ForContext(); _recacheTimer = new Timer(Recache, null, Timeout.Infinite, Timeout.Infinite); // create the timer as disabled. @@ -80,11 +83,6 @@ public void Recache() _recacheTimer.Change(_config.UpdateInterval * 1000, Timeout.Infinite); // reset the recache timer. } - - private void Recache(object state) - { - Recache(); - } private void RecacheGlobal() { @@ -97,5 +95,10 @@ private void RecacheGlobal() MinerCount += pool.MinerManager.Count; } } + + private void Recache(object state) + { + Recache(); + } } } diff --git a/src/CoiniumServ/Utils/Versions/VersionInfo.cs b/src/CoiniumServ/Utils/Versions/VersionInfo.cs index d7a9ce419..9c00e625a 100644 --- a/src/CoiniumServ/Utils/Versions/VersionInfo.cs +++ b/src/CoiniumServ/Utils/Versions/VersionInfo.cs @@ -41,7 +41,7 @@ public static class Assembly /// /// Main assemby version. /// - public const string Version = "0.1.4.*"; + public const string Version = "0.1.5.*"; } } } diff --git a/src/CoiniumServ/config/config-example.json b/src/CoiniumServ/config/config-example.json index d184f67b4..ff256b05e 100644 --- a/src/CoiniumServ/config/config-example.json +++ b/src/CoiniumServ/config/config-example.json @@ -9,6 +9,18 @@ "name": "CoiniumServ.com" }, + # ------------------------------- + # Statistics Configuration + # ------------------------------- + + # updateInterval: interval for recaching statistics. + # hashrateWindow: how many seconds worth of shares should be gathered to generate hashrate. + + "statistics": { + "updateInterval": 60, + "hashrateWindow": 300 + }, + # ------------------------------- # Website Configuration # ------------------------------- @@ -16,22 +28,14 @@ # enabled: set this true to enable frontend. # bind: interface to bind webserver. # port: port to listen for http connections. - # stats: - # updateInterval: interval for recaching statistics. - # hashrateWindow: how many seconds worth of shares should be gathered to generate hashrate. # backend: # enabled: set this true to enable admin backend. # password: password for enabling administrator - # website configuration "website": { "enabled": true, "bind": "127.0.0.1", "port": 80, - "stats": { - "updateInterval": 60, - "hashrateWindow": 300 - }, "backend" : { "metrics": { "enabled": false diff --git a/src/CoiniumServ/web.config b/src/CoiniumServ/web.config new file mode 100644 index 000000000..0f0a3f190 --- /dev/null +++ b/src/CoiniumServ/web.config @@ -0,0 +1,25 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/CoiniumServ/web/default/index.cshtml b/src/CoiniumServ/web/default/index.cshtml index bf98d3d4b..5535f11c6 100644 --- a/src/CoiniumServ/web/default/index.cshtml +++ b/src/CoiniumServ/web/default/index.cshtml @@ -54,7 +54,7 @@
- +
@Model.Statistics.Pools.Count
@@ -100,7 +100,7 @@
-

Per Algorithm Statistics

+

Per Algorithm Statistics

@@ -133,12 +133,11 @@
-
-

Per Pool Statistics

+

Per Pool Statistics

@@ -152,6 +151,7 @@ Workers Algorithm Current Block + Status @@ -160,14 +160,14 @@ @pool.Config.Coin.Name @pool.Hashrate.GetReadableHashrate() - @pool.NetworkStats.Hashrate.GetReadableHashrate() - @pool.NetworkStats.Difficulty + @pool.NetworkInfo.Hashrate.GetReadableHashrate() + @pool.NetworkInfo.Difficulty @pool.MinerManager.Count @pool.Config.Coin.Algorithm - @pool.NetworkStats.Round + @pool.NetworkInfo.Round + @(pool.NetworkInfo.Healthy ? "healty" : "warnings!") - } - + }
diff --git a/src/CoiniumServ/web/default/pool.cshtml b/src/CoiniumServ/web/default/pool.cshtml index 274cf278d..e7b39885d 100644 --- a/src/CoiniumServ/web/default/pool.cshtml +++ b/src/CoiniumServ/web/default/pool.cshtml @@ -5,67 +5,112 @@ @{ Layout = "layout.cshtml"; }
-
-
+ + -
-
+
+
-

Block Stats

+

Block Stats

@@ -77,7 +122,7 @@
-

Latest Blocks

+

Latest Blocks

diff --git a/src/Tests/Jobs/JobTests.cs b/src/Tests/Jobs/JobTests.cs index 345289e5f..0fcb082dc 100644 --- a/src/Tests/Jobs/JobTests.cs +++ b/src/Tests/Jobs/JobTests.cs @@ -197,7 +197,7 @@ public void TestJob() job.EncodedDifficulty.Should().Equal("1d2bd7c3"); // test the current time - job.nTime.Should().Equal("539ee666"); + job.NTime.Should().Equal("539ee666"); // test the clean jobs flag job.CleanJobs.Should().Equal(true); diff --git a/src/Tests/Pools/PoolTests.cs b/src/Tests/Pools/PoolTests.cs index fe99d5a82..bf51d188a 100644 --- a/src/Tests/Pools/PoolTests.cs +++ b/src/Tests/Pools/PoolTests.cs @@ -22,6 +22,7 @@ #endregion using System; +using CoiniumServ.Configuration; using CoiniumServ.Daemon; using CoiniumServ.Daemon.Responses; using CoiniumServ.Factories; @@ -37,6 +38,7 @@ public class PoolTests // object mocks. private readonly IObjectFactory _objectFactory; private readonly IDaemonClient _daemonClient; + private readonly IConfigManager _configManager; private readonly IPoolConfig _config; /// @@ -47,6 +49,9 @@ public PoolTests() // factory mockup. _objectFactory = Substitute.For(); + // config-manager mockup + _configManager = Substitute.For(); + // pool-config mockup. _config = Substitute.For(); _config.Daemon.Valid.Returns(true); @@ -64,7 +69,7 @@ public PoolTests() [Fact] public void ConstructorTest_NonNullParams_ShouldSuccess() { - var pool = new Pool(_config, _objectFactory); // create the pool instance. + var pool = new Pool(_config,_configManager, _objectFactory); // create the pool instance. pool.Should().Not.Be.Null(); pool.InstanceId.Should().Be.GreaterThan((UInt32)0); // pool should be already created an instance id. @@ -78,12 +83,17 @@ public void ConstructorTest_NullParams_ShouldThrowException() { Assert.Throws(() => { - new Pool(null, _objectFactory); + new Pool(null, _configManager, _objectFactory); }).Message.Should().Contain("poolConfig"); Assert.Throws(() => { - new Pool(_config, null); + new Pool(_config, null, _objectFactory); + }).Message.Should().Contain("configManager"); + + Assert.Throws(() => + { + new Pool(_config, _configManager, null); }).Message.Should().Contain("objectFactory"); } }