From cc8c5a65d77b53c901c1ef370d7aa777404f5bb0 Mon Sep 17 00:00:00 2001 From: bonesoul Date: Thu, 7 Aug 2014 16:29:15 +0300 Subject: [PATCH 1/2] Improved ShareManager:CheckBlock() - it'll now check if submitted block has correct transaction hash & pool output reported by the coin daemon. --- src/CoiniumServ/Shares/ShareManager.cs | 39 +++++++++++++++++++------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/CoiniumServ/Shares/ShareManager.cs b/src/CoiniumServ/Shares/ShareManager.cs index 6f8e2a343..5237ecb3c 100644 --- a/src/CoiniumServ/Shares/ShareManager.cs +++ b/src/CoiniumServ/Shares/ShareManager.cs @@ -28,6 +28,7 @@ using CoiniumServ.Daemon; using CoiniumServ.Jobs.Tracker; using CoiniumServ.Persistance; +using CoiniumServ.Pools; using CoiniumServ.Pools.Config; using CoiniumServ.Server.Mining.Stratum; using CoiniumServ.Server.Mining.Stratum.Errors; @@ -49,6 +50,8 @@ public class ShareManager : IShareManager private readonly IStorage _storage; + private readonly IPoolConfig _poolConfig; + private readonly ILogger _logger; /// @@ -60,6 +63,7 @@ public class ShareManager : IShareManager /// public ShareManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJobTracker jobTracker, IStorage storage) { + _poolConfig = poolConfig; _daemonClient = daemonClient; _jobTracker = jobTracker; _storage = storage; @@ -161,10 +165,12 @@ private void HandleInvalidShare(IShare share) private bool SubmitBlock(IShare share) { + // we should try different submission techniques and probably more then once: https://github.com/ahmedbodi/stratum-mining/blob/master/lib/bitcoin_rpc.py#L65-123 + try { _daemonClient.SubmitBlock(share.BlockHex.ToHexString()); - var isAccepted = CheckIfBlockAccepted(share); + var isAccepted = CheckBlock(share); _logger.Information( isAccepted @@ -181,30 +187,43 @@ private bool SubmitBlock(IShare share) } } - private bool CheckIfBlockAccepted(IShare share) + private bool CheckBlock(IShare share) { try { var block = _daemonClient.GetBlock(share.BlockHash.ToHexString()); // query the block. - // generation transaction (the first one) in a block is simply calculated by coinbase hash. - var generationTransactionHash = share.CoinbaseHash.Bytes.ReverseBuffer().ToHexString(); + // calculate our generation transactions's hash + var genTxHash = share.CoinbaseHash.Bytes.ReverseBuffer().ToHexString(); - // make sure our generation transaction matches what the coin daemon reports. - if (generationTransactionHash != block.Tx.First()) + // read the very first (generation transaction) of the block + var initialTx = block.Tx.First(); + + // make sure our calculated generation transaction hash matches the very first transaction reported for block by coin daemon. + if (genTxHash != initialTx) { - _logger.Fatal("Share's generation transaction hash is different then what coin daemon reports. Possible kicked block!"); - Debugger.Break(); // TODO: diagnose the case as this may eventually allow us to remove kicked blocks processing from payments processor and so. + Log.Debug("Kicked submitted block {0} because reported generation transaction hash [{1:l}] doesn't match our calculated one [{2:l}]", initialTx, genTxHash); + return false; } - // todo: also add pool-wallet address check as in payment-processor. + // also make sure the transaction includes our pool wallet address. + var transaction = _daemonClient.GetTransaction(initialTx); + + // check if the transaction includes output for the configured central pool wallet address. + var gotPoolOutput = transaction.Details.Any(test => test.Address == _poolConfig.Wallet.Adress); + + if (!gotPoolOutput) + { + Log.Debug("Kicked submitted block {0} because generation transaction doesn't contain an output for pool's central wallet address: {1:l]", share.Height, _poolConfig.Wallet.Adress); + return false; + } share.SetFoundBlock(block); // assign the block to share. return true; } catch (Exception e) { - _logger.Error(e, "Get block failed - height: {0}, hash: {1:l}", share.Height, share.BlockHash); + _logger.Error("Submitted block does not exist: {0}, hash: {1:l}, {2:l}", share.Height, share.BlockHash, e.Message); return false; } } From 2671f95eb56f8ea791f930f266f20c473d996e25 Mon Sep 17 00:00:00 2001 From: bonesoul Date: Thu, 7 Aug 2014 18:32:30 +0300 Subject: [PATCH 2/2] Implemented block-processor to query blocks. renamed blocks.cs in statistics as blockcount.cs. removed 'kicked' block concept completely. cleaned persisted block structures, we just have ipersistblock.cs now. removed hash candicate structure. also improved submitblock logic within sharemanager. --- src/CoiniumServ/Blocks/BlockProcessor.cs | 98 +++++++++++++ .../IBlockProcessor.cs} | 16 +-- src/CoiniumServ/CoiniumServ.csproj | 18 +-- src/CoiniumServ/Factories/IObjectFactory.cs | 11 +- src/CoiniumServ/Factories/ObjectFactory.cs | 26 +++- src/CoiniumServ/Payments/IPaymentRound.cs | 2 +- src/CoiniumServ/Payments/PaymentProcessor.cs | 133 ++++++++++-------- src/CoiniumServ/Payments/PaymentRound.cs | 4 +- .../Persistance/Blocks/BlockStatus.cs | 1 - .../Persistance/Blocks/ConfirmedBlock.cs | 53 ------- .../Persistance/Blocks/HashCandidate.cs | 45 ------ .../Persistance/Blocks/IConfirmedBlock.cs | 29 ---- .../Persistance/Blocks/IFinalizedBlock.cs | 28 ---- .../Persistance/Blocks/IHashCandidate.cs | 33 ----- .../Persistance/Blocks/IKickedBlock.cs | 29 ---- .../Persistance/Blocks/IOrphanedBlock.cs | 29 ---- .../Persistance/Blocks/IPersistedBlock.cs | 4 +- .../Persistance/Blocks/OrphanedBlock.cs | 53 ------- .../Persistance/Blocks/PendingBlock.cs | 108 -------------- .../{KickedBlock.cs => PersistedBlock.cs} | 32 +++-- src/CoiniumServ/Persistance/IStorage.cs | 2 +- src/CoiniumServ/Persistance/Redis/Redis.cs | 81 ++--------- src/CoiniumServ/Pools/Pool.cs | 36 +++-- .../Repository/Registries/ClassRegistry.cs | 4 +- src/CoiniumServ/Shares/ShareManager.cs | 85 +++++------ .../Statistics/{Blocks.cs => BlocksCount.cs} | 4 +- .../{IBlocks.cs => IBlocksCount.cs} | 2 +- src/CoiniumServ/Statistics/IPerPool.cs | 2 +- src/CoiniumServ/Statistics/PerPool.cs | 4 +- src/CoiniumServ/web/default/pool.cshtml | 1 - 30 files changed, 310 insertions(+), 663 deletions(-) create mode 100644 src/CoiniumServ/Blocks/BlockProcessor.cs rename src/CoiniumServ/{Persistance/Blocks/IPendingBlock.cs => Blocks/IBlockProcessor.cs} (75%) delete mode 100644 src/CoiniumServ/Persistance/Blocks/ConfirmedBlock.cs delete mode 100644 src/CoiniumServ/Persistance/Blocks/HashCandidate.cs delete mode 100644 src/CoiniumServ/Persistance/Blocks/IConfirmedBlock.cs delete mode 100644 src/CoiniumServ/Persistance/Blocks/IFinalizedBlock.cs delete mode 100644 src/CoiniumServ/Persistance/Blocks/IHashCandidate.cs delete mode 100644 src/CoiniumServ/Persistance/Blocks/IKickedBlock.cs delete mode 100644 src/CoiniumServ/Persistance/Blocks/IOrphanedBlock.cs delete mode 100644 src/CoiniumServ/Persistance/Blocks/OrphanedBlock.cs delete mode 100644 src/CoiniumServ/Persistance/Blocks/PendingBlock.cs rename src/CoiniumServ/Persistance/Blocks/{KickedBlock.cs => PersistedBlock.cs} (68%) rename src/CoiniumServ/Statistics/{Blocks.cs => BlocksCount.cs} (94%) rename src/CoiniumServ/Statistics/{IBlocks.cs => IBlocksCount.cs} (95%) diff --git a/src/CoiniumServ/Blocks/BlockProcessor.cs b/src/CoiniumServ/Blocks/BlockProcessor.cs new file mode 100644 index 000000000..954ddd449 --- /dev/null +++ b/src/CoiniumServ/Blocks/BlockProcessor.cs @@ -0,0 +1,98 @@ +#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 System; +using System.Linq; +using CoiniumServ.Daemon; +using CoiniumServ.Daemon.Exceptions; +using CoiniumServ.Daemon.Responses; +using CoiniumServ.Pools.Config; +using Serilog; + +namespace CoiniumServ.Blocks +{ + public class BlockProcessor:IBlockProcessor + { + private readonly IDaemonClient _daemonClient; + + private readonly IPoolConfig _poolConfig; + + private readonly ILogger _logger; + + public BlockProcessor(IPoolConfig poolConfig, IDaemonClient daemonClient) + { + _poolConfig = poolConfig; + _daemonClient = daemonClient; + _logger = Log.ForContext().ForContext("Component", poolConfig.Coin.Name); + } + + public bool GetBlockDetails(string blockHash, out Block block, out Transaction generationTransaction) + { + block = null; + generationTransaction = null; + + try + { + // query the block. + block = _daemonClient.GetBlock(blockHash); + + // read the very first (generation transaction) of the block + var generationTx = block.Tx.First(); + + // also make sure the transaction includes our pool wallet address. + generationTransaction = _daemonClient.GetTransaction(generationTx); + + return true; + } + catch (RpcException e) + { + _logger.Error("Queried block does not exist {0:l}, {1:l}", blockHash, e.Message); + return false; + } + } + + public bool CheckGenTxHash(Block block, string expectedTxHash) + { + // get the generation transaction for the block. + var genTx = block.Tx.First(); + + if (expectedTxHash == genTx) + return true; + + Log.Debug("Queried block {0:l} doesn't seem to belong us as reported generation transaction hash [{1:l}] doesn't match our expected one [{2:l}]", block.Hash, genTx, expectedTxHash); + return false; + } + + public bool ContainsPoolOutput(Transaction transaction) + { + // check if the transaction includes output for the configured central pool wallet address. + var gotPoolOutput = transaction.Details.Any(x => x.Address == _poolConfig.Wallet.Adress); + + if (gotPoolOutput) // if we got the correct pool output + return true; // then the block seems to belong us. + + Log.Debug("Queried block doesn't seem to belong us as generation transaction doesn't contain an output for pool's central wallet address: {0:}", _poolConfig.Wallet.Adress); + return false; + } + } +} diff --git a/src/CoiniumServ/Persistance/Blocks/IPendingBlock.cs b/src/CoiniumServ/Blocks/IBlockProcessor.cs similarity index 75% rename from src/CoiniumServ/Persistance/Blocks/IPendingBlock.cs rename to src/CoiniumServ/Blocks/IBlockProcessor.cs index f91adf0e3..011f51bd2 100644 --- a/src/CoiniumServ/Persistance/Blocks/IPendingBlock.cs +++ b/src/CoiniumServ/Blocks/IBlockProcessor.cs @@ -21,20 +21,16 @@ // #endregion -using System.Collections.Generic; +using CoiniumServ.Daemon.Responses; -namespace CoiniumServ.Persistance.Blocks +namespace CoiniumServ.Blocks { - public interface IPendingBlock: IPersistedBlock + public interface IBlockProcessor { - bool IsFinalized { get; } + bool GetBlockDetails(string blockHash, out Block block, out Transaction generationTransaction); - IFinalizedBlock Finalized { get; } + bool CheckGenTxHash(Block block, string expectedTxHash); - List Candidates { get; } - - void AddHashCandidate(IHashCandidate hash); - - void Check(); + bool ContainsPoolOutput(Transaction transaction); } } diff --git a/src/CoiniumServ/CoiniumServ.csproj b/src/CoiniumServ/CoiniumServ.csproj index 84b26803b..c27f682c4 100644 --- a/src/CoiniumServ/CoiniumServ.csproj +++ b/src/CoiniumServ/CoiniumServ.csproj @@ -109,6 +109,8 @@ + + @@ -143,6 +145,7 @@ + @@ -153,8 +156,8 @@ - - + + @@ -200,18 +203,7 @@ - - - - - - - - - - - diff --git a/src/CoiniumServ/Factories/IObjectFactory.cs b/src/CoiniumServ/Factories/IObjectFactory.cs index bd8d3b256..7af91c6a1 100644 --- a/src/CoiniumServ/Factories/IObjectFactory.cs +++ b/src/CoiniumServ/Factories/IObjectFactory.cs @@ -22,6 +22,7 @@ #endregion using CoiniumServ.Banning; +using CoiniumServ.Blocks; using CoiniumServ.Cryptology.Algorithms; using CoiniumServ.Daemon; using CoiniumServ.Jobs.Manager; @@ -78,9 +79,11 @@ IJobManager GetJobManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJ IJobTracker GetJobTracker(); - IShareManager GetShareManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJobTracker jobTracker, IStorage storage); + IShareManager GetShareManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJobTracker jobTracker, IStorage storage, IBlockProcessor blockProcessor); - IPaymentProcessor GetPaymentProcessor(IPoolConfig poolConfig, IDaemonClient daemonClient, IStorage storage); + IPaymentProcessor GetPaymentProcessor(IPoolConfig poolConfig, IDaemonClient daemonClient, IStorage storage, IBlockProcessor blockProcessor); + + IBlockProcessor GetBlockProcessor(IPoolConfig poolConfig, IDaemonClient daemonClient); IBanManager GetBanManager(IPoolConfig poolConfig, IShareManager shareManager); @@ -98,9 +101,9 @@ IJobManager GetJobManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJ IPools GetPoolStats(); - IPerPool GetPerPoolStats(IPoolConfig poolConfig, IDaemonClient daemonClient, IMinerManager minerManager, IHashAlgorithm hashAlgorithm, IBlocks blockStatistics, IStorage storage); + IPerPool GetPerPoolStats(IPoolConfig poolConfig, IDaemonClient daemonClient, IMinerManager minerManager, IHashAlgorithm hashAlgorithm, IBlocksCount blockStatistics, IStorage storage); - IBlocks GetBlockStats(ILatestBlocks latestBlocks, IStorage storage); + IBlocksCount GetBlockStats(ILatestBlocks latestBlocks, IStorage storage); ILatestBlocks GetLatestBlocks(IStorage storage); diff --git a/src/CoiniumServ/Factories/ObjectFactory.cs b/src/CoiniumServ/Factories/ObjectFactory.cs index 3f7bec4a3..99fe822c4 100644 --- a/src/CoiniumServ/Factories/ObjectFactory.cs +++ b/src/CoiniumServ/Factories/ObjectFactory.cs @@ -22,6 +22,7 @@ #endregion using CoiniumServ.Banning; +using CoiniumServ.Blocks; using CoiniumServ.Cryptology.Algorithms; using CoiniumServ.Daemon; using CoiniumServ.Jobs.Manager; @@ -145,7 +146,7 @@ public IJobTracker GetJobTracker() return _applicationContext.Container.Resolve(); } - public IShareManager GetShareManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJobTracker jobTracker, IStorage storage) + public IShareManager GetShareManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJobTracker jobTracker, IStorage storage, IBlockProcessor blockProcessor) { var @params = new NamedParameterOverloads { @@ -153,23 +154,36 @@ public IShareManager GetShareManager(IPoolConfig poolConfig, IDaemonClient daemo {"daemonClient", daemonClient}, {"jobTracker", jobTracker}, {"storage", storage}, + {"blockProcessor", blockProcessor} }; return _applicationContext.Container.Resolve(@params); } - public IPaymentProcessor GetPaymentProcessor(IPoolConfig poolConfig, IDaemonClient daemonClient, IStorage storage) + public IPaymentProcessor GetPaymentProcessor(IPoolConfig poolConfig, IDaemonClient daemonClient, IStorage storage, IBlockProcessor blockProcessor) { var @params = new NamedParameterOverloads { {"poolConfig", poolConfig}, {"daemonClient", daemonClient}, - {"storage", storage}, + {"storage", storage}, + {"blockProcessor", blockProcessor}, }; return _applicationContext.Container.Resolve(@params); } + public IBlockProcessor GetBlockProcessor(IPoolConfig poolConfig, IDaemonClient daemonClient) + { + var @params = new NamedParameterOverloads + { + {"poolConfig", poolConfig}, + {"daemonClient", daemonClient}, + }; + + return _applicationContext.Container.Resolve(@params); + } + public IBanManager GetBanManager(IPoolConfig poolConfig, IShareManager shareManager) { var @params = new NamedParameterOverloads @@ -216,7 +230,7 @@ public IPools GetPoolStats() return _applicationContext.Container.Resolve(); } - public IPerPool GetPerPoolStats(IPoolConfig poolConfig, IDaemonClient daemonClient, IMinerManager minerManager, IHashAlgorithm hashAlgorithm, IBlocks blockStatistics, IStorage storage) + public IPerPool GetPerPoolStats(IPoolConfig poolConfig, IDaemonClient daemonClient, IMinerManager minerManager, IHashAlgorithm hashAlgorithm, IBlocksCount blockStatistics, IStorage storage) { var @params = new NamedParameterOverloads { @@ -241,7 +255,7 @@ public ILatestBlocks GetLatestBlocks(IStorage storage) return _applicationContext.Container.Resolve(@params); } - public IBlocks GetBlockStats(ILatestBlocks latestBlocks, IStorage storage) + public IBlocksCount GetBlockStats(ILatestBlocks latestBlocks, IStorage storage) { var @params = new NamedParameterOverloads { @@ -249,7 +263,7 @@ public IBlocks GetBlockStats(ILatestBlocks latestBlocks, IStorage storage) {"storage", storage}, }; - return _applicationContext.Container.Resolve(@params); + return _applicationContext.Container.Resolve(@params); } #endregion diff --git a/src/CoiniumServ/Payments/IPaymentRound.cs b/src/CoiniumServ/Payments/IPaymentRound.cs index e7626a098..f1e760458 100644 --- a/src/CoiniumServ/Payments/IPaymentRound.cs +++ b/src/CoiniumServ/Payments/IPaymentRound.cs @@ -28,7 +28,7 @@ namespace CoiniumServ.Payments { public interface IPaymentRound { - IFinalizedBlock Block { get; } + IPersistedBlock Block { get; } Dictionary Shares { get; } diff --git a/src/CoiniumServ/Payments/PaymentProcessor.cs b/src/CoiniumServ/Payments/PaymentProcessor.cs index db242ca93..9e420c0b6 100644 --- a/src/CoiniumServ/Payments/PaymentProcessor.cs +++ b/src/CoiniumServ/Payments/PaymentProcessor.cs @@ -26,10 +26,13 @@ using System.Diagnostics; using System.Linq; using System.Threading; +using CoiniumServ.Blocks; using CoiniumServ.Daemon; using CoiniumServ.Daemon.Exceptions; +using CoiniumServ.Daemon.Responses; using CoiniumServ.Persistance; using CoiniumServ.Persistance.Blocks; +using CoiniumServ.Pools; using CoiniumServ.Pools.Config; using Serilog; @@ -42,8 +45,13 @@ public class PaymentProcessor : IPaymentProcessor public bool IsEnabled { get; private set; } private readonly IDaemonClient _daemonClient; + private readonly IStorage _storage; + private IPaymentConfig _config; + + private readonly IBlockProcessor _blockProcessor; + private Timer _timer; private Int32 _precision; // coin's precision. @@ -54,15 +62,19 @@ public class PaymentProcessor : IPaymentProcessor private readonly Stopwatch _stopWatch = new Stopwatch(); private readonly IWalletConfig _walletConfig; + private string _poolAccount = string.Empty; private readonly ILogger _logger; - public PaymentProcessor(IPoolConfig poolConfig, IDaemonClient daemonClient, IStorage storage ) + public PaymentProcessor(IPoolConfig poolConfig, IDaemonClient daemonClient, IStorage storage, IBlockProcessor blockProcessor) { _daemonClient = daemonClient; _storage = storage; + _blockProcessor = blockProcessor; + _walletConfig = poolConfig.Wallet; + _logger = Log.ForContext().ForContext("Component", poolConfig.Coin.Name); } @@ -102,10 +114,9 @@ private void RunPayments(object state) { _stopWatch.Start(); - var pendingBlocks = _storage.GetPendingBlocks(); // get all the pending blocks. - var finalizedBlocks = GetFinalizedBlocks(pendingBlocks); - - var rounds = GetPaymentRounds(finalizedBlocks); // get the list of rounds that should be paid. + var pendingBlocks = _storage.GetBlocks(BlockStatus.Pending); // get all the pending blocks. + var nonPendingBlocks = GetNonPendingBlocks(pendingBlocks); // get the confirmed blocks that are either orphan or mature. + var rounds = GetPaymentRounds(nonPendingBlocks); // get the list of rounds that should be paid. AssignSharesToRounds(rounds); // process the rounds, calculate shares and payouts per rounds. var previousBalances = GetPreviousBalances(); // get previous balances of workers. var workerBalances = CalculateRewards(rounds, previousBalances); // calculate the payments. @@ -215,7 +226,6 @@ private void ProcessRounds(IEnumerable rounds) _storage.DeleteShares(round); // delete the associated shares. _storage.MoveBlock(round); // move pending block to appropriate place. break; - case BlockStatus.Kicked: case BlockStatus.Orphaned: _storage.MoveSharesToCurrentRound(round); // move shares to current round so the work of miners aren't gone to void. _storage.MoveBlock(round); // move pending block to appropriate place. @@ -239,79 +249,82 @@ private void AssignSharesToRounds(IList rounds) } } - private IList GetFinalizedBlocks(IEnumerable blocks) + private IEnumerable GetNonPendingBlocks(IEnumerable blocks) { - var finalizedBlocks = new List(); + var nonPendingBlocks = new List(); - foreach (var block in blocks) + foreach (var item in blocks) { - foreach (var candidate in block.Candidates) - { - CheckCandidates(candidate); - } - - block.Check(); + var block = item; // get a local copy of the block + QueryBlock(ref block); // check the block. - if(block.IsFinalized) - finalizedBlocks.Add(block.Finalized); + if (!block.IsPending) // only add non-pending blocks to list. + nonPendingBlocks.Add(block); } - return finalizedBlocks; + return nonPendingBlocks; } - private IEnumerable GetConfirmedBlocks(IEnumerable blocks) + private void QueryBlock(ref IPersistedBlock block) { - return blocks.OfType().ToList(); - } + // query the block against coin daemon and see if seems all good. + Block blockInfo; // the block repsonse from coin daemon. + Transaction genTx; // generation transaction response from coin daemon. + var exists = _blockProcessor.GetBlockDetails(block.BlockHash, out blockInfo, out genTx); // query the coin daemon for the block details. - private void CheckCandidates(IHashCandidate candidate) - { - try + if (!exists) // make sure the block exists. { - // get the transaction. - var transaction = _daemonClient.GetTransaction(candidate.TransactionHash); + block.Status = BlockStatus.Orphaned; + return; + } - // total amount of coins contained in the block. - candidate.Amount = transaction.Details.Sum(output => (decimal)output.Amount); + // calculate our expected generation transactions's hash + var expectedTxHash = block.TransactionHash; - // get the output transaction that targets pools central wallet. - var poolOutput = transaction.Details.FirstOrDefault(output => output.Address == _walletConfig.Adress); + // make sure our calculated and reported generation tx hashes match. + if (!_blockProcessor.CheckGenTxHash(blockInfo, expectedTxHash)) + { + block.Status = BlockStatus.Orphaned; + return; + } - // make sure output for the pool central wallet exists - if (poolOutput == null) - { - candidate.Status = BlockStatus.Kicked; // kick the hash. - candidate.Reward = 0; - } - else - { - switch (poolOutput.Category) - { - case "immature": - candidate.Status = BlockStatus.Pending; - break; - case "orphan": - candidate.Status = BlockStatus.Orphaned; - break; - case "generate": - candidate.Status = BlockStatus.Confirmed; - break; - default: - candidate.Status = BlockStatus.Pending; - break; - } + // make sure the blocks generation transaction contains our central pool wallet address + if (!_blockProcessor.ContainsPoolOutput(genTx)) + { + block.Status = BlockStatus.Orphaned; + return; + } - candidate.Reward = (decimal) poolOutput.Amount; - } + // get the output transaction that targets pools central wallet. + var poolOutput = genTx.Details.FirstOrDefault(output => output.Address == _walletConfig.Adress); + + // make sure we have a valid reference to poolOutput + if (poolOutput == null) + { + block.Status = BlockStatus.Orphaned; + return; } - catch (RpcException) + + switch (poolOutput.Category) { - candidate.Status = BlockStatus.Kicked; // kick the hash. - candidate.Reward = 0; - } + case "immature": + block.Status = BlockStatus.Pending; + break; + case "orphan": + block.Status = BlockStatus.Orphaned; + break; + case "generate": + block.Status = BlockStatus.Confirmed; + break; + } + + // TODO: add back these. + // total amount of coins contained in the block. + // candidate.Amount = transaction.Details.Sum(output => (decimal)output.Amount); + // candidate.Reward = (decimal) poolOutput.Amount; } - private IList GetPaymentRounds(IEnumerable blocks) + private IList GetPaymentRounds(IEnumerable blocks) { var rounds = new List(); diff --git a/src/CoiniumServ/Payments/PaymentRound.cs b/src/CoiniumServ/Payments/PaymentRound.cs index 6961a3801..eb08f1d04 100644 --- a/src/CoiniumServ/Payments/PaymentRound.cs +++ b/src/CoiniumServ/Payments/PaymentRound.cs @@ -29,12 +29,12 @@ namespace CoiniumServ.Payments { public class PaymentRound:IPaymentRound { - public IFinalizedBlock Block { get; private set; } + public IPersistedBlock Block { get; private set; } public Dictionary Shares { get; private set; } public Dictionary Payouts { get; private set; } - public PaymentRound(IFinalizedBlock block) + public PaymentRound(IPersistedBlock block) { Block = block; Payouts = new Dictionary(); diff --git a/src/CoiniumServ/Persistance/Blocks/BlockStatus.cs b/src/CoiniumServ/Persistance/Blocks/BlockStatus.cs index 1fe25c1c1..d56e88ff9 100644 --- a/src/CoiniumServ/Persistance/Blocks/BlockStatus.cs +++ b/src/CoiniumServ/Persistance/Blocks/BlockStatus.cs @@ -25,7 +25,6 @@ namespace CoiniumServ.Persistance.Blocks public enum BlockStatus { Pending, - Kicked, Orphaned, Confirmed } diff --git a/src/CoiniumServ/Persistance/Blocks/ConfirmedBlock.cs b/src/CoiniumServ/Persistance/Blocks/ConfirmedBlock.cs deleted file mode 100644 index 9402d8f74..000000000 --- a/src/CoiniumServ/Persistance/Blocks/ConfirmedBlock.cs +++ /dev/null @@ -1,53 +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 -namespace CoiniumServ.Persistance.Blocks -{ - public class ConfirmedBlock:IConfirmedBlock - { - public uint Height { get; private set; } - public BlockStatus Status { get; private set; } - public string BlockHash { get; private set; } - public string TransactionHash { get; private set; } - public decimal Reward { get; set; } - public decimal Amount { get; set; } - - public ConfirmedBlock(uint height, string blockHash, string transactionHash, decimal amount, decimal reward) - { - Height = height; - BlockHash = blockHash; - TransactionHash = transactionHash; - Amount = amount; - Reward = reward; - Status = BlockStatus.Confirmed; - } - - public ConfirmedBlock(uint height, IHashCandidate candidate) - : this(height, candidate.BlockHash, candidate.TransactionHash, candidate.Amount, candidate.Reward) - { } - - public override string ToString() - { - return string.Format("Height: {0}, Status: Confirmed.", Height); - } - } -} diff --git a/src/CoiniumServ/Persistance/Blocks/HashCandidate.cs b/src/CoiniumServ/Persistance/Blocks/HashCandidate.cs deleted file mode 100644 index 93b6ec30d..000000000 --- a/src/CoiniumServ/Persistance/Blocks/HashCandidate.cs +++ /dev/null @@ -1,45 +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 -namespace CoiniumServ.Persistance.Blocks -{ - public class HashCandidate : IHashCandidate - { - public string BlockHash { get; private set; } - public string TransactionHash { get; private set; } - public decimal Amount { get; set; } - public decimal Reward { get; set; } - public BlockStatus Status { get; set; } - - public HashCandidate(string blockHash, string transactionHash) - { - BlockHash = blockHash; - TransactionHash = transactionHash; - Status = BlockStatus.Pending; - } - - public override string ToString() - { - return string.Format("Status: {0}, Block Hash: {1}, Transaction Hash: {2}", Status, BlockHash, TransactionHash); - } - } -} diff --git a/src/CoiniumServ/Persistance/Blocks/IConfirmedBlock.cs b/src/CoiniumServ/Persistance/Blocks/IConfirmedBlock.cs deleted file mode 100644 index 48110a899..000000000 --- a/src/CoiniumServ/Persistance/Blocks/IConfirmedBlock.cs +++ /dev/null @@ -1,29 +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 - -namespace CoiniumServ.Persistance.Blocks -{ - public interface IConfirmedBlock:IFinalizedBlock - { - } -} diff --git a/src/CoiniumServ/Persistance/Blocks/IFinalizedBlock.cs b/src/CoiniumServ/Persistance/Blocks/IFinalizedBlock.cs deleted file mode 100644 index c7b507f5b..000000000 --- a/src/CoiniumServ/Persistance/Blocks/IFinalizedBlock.cs +++ /dev/null @@ -1,28 +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 -namespace CoiniumServ.Persistance.Blocks -{ - public interface IFinalizedBlock:IPersistedBlock - { - } -} diff --git a/src/CoiniumServ/Persistance/Blocks/IHashCandidate.cs b/src/CoiniumServ/Persistance/Blocks/IHashCandidate.cs deleted file mode 100644 index f6fd48ae7..000000000 --- a/src/CoiniumServ/Persistance/Blocks/IHashCandidate.cs +++ /dev/null @@ -1,33 +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 -namespace CoiniumServ.Persistance.Blocks -{ - public interface IHashCandidate - { - string BlockHash { get; } - string TransactionHash { get; } - decimal Amount { get; set; } - decimal Reward { get; set; } - BlockStatus Status { get; set; } - } -} diff --git a/src/CoiniumServ/Persistance/Blocks/IKickedBlock.cs b/src/CoiniumServ/Persistance/Blocks/IKickedBlock.cs deleted file mode 100644 index 75957c8ff..000000000 --- a/src/CoiniumServ/Persistance/Blocks/IKickedBlock.cs +++ /dev/null @@ -1,29 +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 - -namespace CoiniumServ.Persistance.Blocks -{ - public interface IKickedBlock:IFinalizedBlock - { - } -} diff --git a/src/CoiniumServ/Persistance/Blocks/IOrphanedBlock.cs b/src/CoiniumServ/Persistance/Blocks/IOrphanedBlock.cs deleted file mode 100644 index af81db90f..000000000 --- a/src/CoiniumServ/Persistance/Blocks/IOrphanedBlock.cs +++ /dev/null @@ -1,29 +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 - -namespace CoiniumServ.Persistance.Blocks -{ - public interface IOrphanedBlock:IFinalizedBlock - { - } -} diff --git a/src/CoiniumServ/Persistance/Blocks/IPersistedBlock.cs b/src/CoiniumServ/Persistance/Blocks/IPersistedBlock.cs index 9904db196..7baf2c469 100644 --- a/src/CoiniumServ/Persistance/Blocks/IPersistedBlock.cs +++ b/src/CoiniumServ/Persistance/Blocks/IPersistedBlock.cs @@ -28,10 +28,12 @@ namespace CoiniumServ.Persistance.Blocks public interface IPersistedBlock { UInt32 Height { get; } - BlockStatus Status { get; } + BlockStatus Status { get; set; } string BlockHash { get; } string TransactionHash { get; } decimal Reward { get; } decimal Amount { get; } + + bool IsPending { get; } } } diff --git a/src/CoiniumServ/Persistance/Blocks/OrphanedBlock.cs b/src/CoiniumServ/Persistance/Blocks/OrphanedBlock.cs deleted file mode 100644 index 20f6e1f14..000000000 --- a/src/CoiniumServ/Persistance/Blocks/OrphanedBlock.cs +++ /dev/null @@ -1,53 +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 -namespace CoiniumServ.Persistance.Blocks -{ - public class OrphanedBlock:IOrphanedBlock - { - public uint Height { get; private set; } - public BlockStatus Status { get; private set; } - public string BlockHash { get; private set; } - public string TransactionHash { get; private set; } - public decimal Reward { get; set; } - public decimal Amount { get; set; } - - public OrphanedBlock(uint height, string blockHash, string transactionHash, decimal amount, decimal reward) - { - Height = height; - BlockHash = blockHash; - TransactionHash = transactionHash; - Reward = reward; - Amount = amount; - Status = BlockStatus.Orphaned; - } - - public OrphanedBlock(uint height, IHashCandidate candidate) - : this(height, candidate.BlockHash, candidate.TransactionHash,candidate.Amount, candidate.Reward) - { } - - public override string ToString() - { - return string.Format("Height: {0}, Status: Orphaned.", Height); - } - } -} diff --git a/src/CoiniumServ/Persistance/Blocks/PendingBlock.cs b/src/CoiniumServ/Persistance/Blocks/PendingBlock.cs deleted file mode 100644 index 7545de775..000000000 --- a/src/CoiniumServ/Persistance/Blocks/PendingBlock.cs +++ /dev/null @@ -1,108 +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 System.Collections.Generic; -using System.Linq; - -namespace CoiniumServ.Persistance.Blocks -{ - public class PendingBlock : IPendingBlock - { - public uint Height { get; private set; } - - public bool IsFinalized - { - get { return Finalized != null; } - } - - public BlockStatus Status - { - get { return IsFinalized ? Finalized.Status : BlockStatus.Pending; } - } - - public string BlockHash - { - get { return IsFinalized ? Finalized.BlockHash : Candidates.First().BlockHash; } - } - - public string TransactionHash - { - get { return IsFinalized ? Finalized.BlockHash : Candidates.First().TransactionHash; } - } - - public decimal Reward - { - get { return IsFinalized ? Finalized.Reward : 0; } - } - - public decimal Amount - { - get { return IsFinalized ? Finalized.Amount : 0; } - } - - public IFinalizedBlock Finalized { get; private set; } - public List Candidates { get; private set; } - - public PendingBlock(uint height) - { - Height = height; - Candidates = new List(); - Finalized = null; - } - - public void AddHashCandidate(IHashCandidate hash) - { - Candidates.Add(hash); - } - - public void Check() - { - var confirmedCandidate = Candidates.FirstOrDefault( x => x.Status == BlockStatus.Confirmed); - - if (confirmedCandidate != null) - { - Finalized = new ConfirmedBlock(Height, confirmedCandidate); - return; - } - - var orphanedCandidate = Candidates.FirstOrDefault(x => x.Status == BlockStatus.Orphaned); - if (orphanedCandidate != null) - { - Finalized = new OrphanedBlock(Height, orphanedCandidate); - return; - } - - var kickedCandidate = Candidates.FirstOrDefault(x => x.Status == BlockStatus.Kicked); - if (kickedCandidate != null) - { - Finalized = new KickedBlock(Height, kickedCandidate); - return; - } - } - - public override string ToString() - { - return string.Format("Height: {0}, Status: {1}.", Height, Finalized == null ? BlockStatus.Pending : Finalized.Status); - } - } -} diff --git a/src/CoiniumServ/Persistance/Blocks/KickedBlock.cs b/src/CoiniumServ/Persistance/Blocks/PersistedBlock.cs similarity index 68% rename from src/CoiniumServ/Persistance/Blocks/KickedBlock.cs rename to src/CoiniumServ/Persistance/Blocks/PersistedBlock.cs index 6a5df9612..076616d7c 100644 --- a/src/CoiniumServ/Persistance/Blocks/KickedBlock.cs +++ b/src/CoiniumServ/Persistance/Blocks/PersistedBlock.cs @@ -20,34 +20,36 @@ // license or white-label it as set out in licenses/commercial.txt. // #endregion + +using System.Diagnostics; + namespace CoiniumServ.Persistance.Blocks { - public class KickedBlock:IKickedBlock + [DebuggerDisplay("Height: {Height}, Status: {Status}")] + public class PersistedBlock:IPersistedBlock { public uint Height { get; private set; } - public BlockStatus Status { get; private set; } + + public BlockStatus Status { get; set; } + public string BlockHash { get; private set; } + public string TransactionHash { get; private set; } - public decimal Reward { get; set; } - public decimal Amount { get; set; } - public KickedBlock(uint height, string blockHash, string transactionHash, decimal amount, decimal reward) + public decimal Reward { get; private set; } + + public decimal Amount { get; private set; } + + public bool IsPending { get { return Status != BlockStatus.Orphaned && Status != BlockStatus.Confirmed; } } + + public PersistedBlock(BlockStatus status, uint height, string blockHash, string transactionHash, decimal amount, decimal reward) { + Status = status; Height = height; BlockHash = blockHash; TransactionHash = transactionHash; Amount = amount; Reward = reward; - Status = BlockStatus.Kicked; - } - - public KickedBlock(uint height, IHashCandidate candidate) - : this(height, candidate.BlockHash, candidate.TransactionHash, candidate.Amount, candidate.Reward) - { } - - public override string ToString() - { - return string.Format("Height: {0}, Status: Kicked.", Height); } } } diff --git a/src/CoiniumServ/Persistance/IStorage.cs b/src/CoiniumServ/Persistance/IStorage.cs index e577a8c6e..85a45a384 100644 --- a/src/CoiniumServ/Persistance/IStorage.cs +++ b/src/CoiniumServ/Persistance/IStorage.cs @@ -51,7 +51,7 @@ public interface IStorage IDictionary GetHashrateData(int since); - IList GetPendingBlocks(); + IEnumerable GetBlocks(BlockStatus status); IDictionary GetAllBlocks(); diff --git a/src/CoiniumServ/Persistance/Redis/Redis.cs b/src/CoiniumServ/Persistance/Redis/Redis.cs index cc4065443..e4c409c71 100644 --- a/src/CoiniumServ/Persistance/Redis/Redis.cs +++ b/src/CoiniumServ/Persistance/Redis/Redis.cs @@ -231,9 +231,6 @@ public void MoveBlock(IPaymentRound round) switch (round.Block.Status) { - case BlockStatus.Kicked: - newKey = string.Format("{0}:blocks:kicked", coin); - break; case BlockStatus.Orphaned: newKey = string.Format("{0}:blocks:orphaned", coin); break; @@ -340,64 +337,22 @@ public IDictionary GetHashrateData(int since) return hashrates; } - public IList GetPendingBlocks() - { - var blocks = new Dictionary(); - - try - { - if (!IsEnabled || !IsConnected) - return blocks.Values.ToList(); - - var coin = _poolConfig.Coin.Name.ToLower(); // the coin we are working on. - var pendingKey = string.Format("{0}:blocks:pending", coin); - - var results = _client.ZRevRangeByScoreWithScores(pendingKey, double.PositiveInfinity, 0, true); - - foreach (var result in results) - { - var item = result.Item1; - var score = result.Item2; - - var data = item.Split(':'); - var blockHash = data[0]; - var transactionHash = data[1]; - var hashCandidate = new HashCandidate(blockHash, transactionHash); - - if (!blocks.ContainsKey((UInt32) score)) - blocks.Add((UInt32) score, new PendingBlock((UInt32) score)); - - var persistedBlock = blocks[(UInt32) score]; - persistedBlock.AddHashCandidate(hashCandidate); - } - } - catch (Exception e) - { - _logger.Error("An exception occured while getting pending blocks: {0:l}", e.Message); - } - - return blocks.Values.ToList(); - } - - private IEnumerable GetFinalizedBlocks(BlockStatus status) + public IEnumerable GetBlocks(BlockStatus status) { - var blocks = new Dictionary(); + var blocks = new Dictionary(); try { if (!IsEnabled || !IsConnected) return blocks.Values.ToList(); - if (status == BlockStatus.Pending) - throw new Exception("Pending is not a valid finalized block status"); - var coin = _poolConfig.Coin.Name.ToLower(); // the coin we are working on. - string key = string.Empty; + var key = string.Empty; switch (status) { - case BlockStatus.Kicked: - key = string.Format("{0}:blocks:kicked", coin); + case BlockStatus.Pending: + key = string.Format("{0}:blocks:pending", coin); break; case BlockStatus.Orphaned: key = string.Format("{0}:blocks:orphaned", coin); @@ -418,23 +373,12 @@ private IEnumerable GetFinalizedBlocks(BlockStatus status) var blockHash = data[0]; var transactionHash = data[1]; - switch (status) - { - case BlockStatus.Kicked: - blocks.Add((UInt32) score, new KickedBlock((UInt32) score, blockHash, transactionHash, 0, 0)); - break; - case BlockStatus.Orphaned: - blocks.Add((UInt32) score,new OrphanedBlock((UInt32) score, blockHash, transactionHash, 0, 0)); - break; - case BlockStatus.Confirmed: - blocks.Add((UInt32) score, new ConfirmedBlock((UInt32) score, blockHash, transactionHash, 0, 0)); - break; - } + blocks.Add((UInt32) score, new PersistedBlock(status, (UInt32) score, blockHash, transactionHash, 0, 0)); } } catch (Exception e) { - _logger.Error("An exception occured while getting finalized blocks: {0:l}", e.Message); + _logger.Error("An exception occured while getting {0:l} blocks: {1:l}", status, e.Message); } return blocks.Values.ToList(); @@ -447,22 +391,17 @@ public IDictionary GetAllBlocks() if (!IsEnabled || !IsConnected) return blocks; - foreach (var block in GetFinalizedBlocks(BlockStatus.Confirmed)) - { - blocks.Add(block.Height, block); - } - - foreach (var block in GetFinalizedBlocks(BlockStatus.Orphaned)) + foreach (var block in GetBlocks(BlockStatus.Confirmed)) { blocks.Add(block.Height, block); } - foreach (var block in GetFinalizedBlocks(BlockStatus.Kicked)) + foreach (var block in GetBlocks(BlockStatus.Orphaned)) { blocks.Add(block.Height, block); } - foreach (var block in GetPendingBlocks()) + foreach (var block in GetBlocks(BlockStatus.Pending)) { blocks.Add(block.Height, block); } diff --git a/src/CoiniumServ/Pools/Pool.cs b/src/CoiniumServ/Pools/Pool.cs index 1b42cb281..2f5d8cac7 100644 --- a/src/CoiniumServ/Pools/Pool.cs +++ b/src/CoiniumServ/Pools/Pool.cs @@ -150,32 +150,42 @@ private void InitDaemon() private void InitManagers() { - var storage = _objectFactory.GetStorage(Storages.Redis, Config); + try + { + var storage = _objectFactory.GetStorage(Storages.Redis, Config); - var paymentProcessor = _objectFactory.GetPaymentProcessor(Config, _daemonClient, storage); - paymentProcessor.Initialize(Config.Payments); + _minerManager = _objectFactory.GetMinerManager(Config, _daemonClient); - _minerManager = _objectFactory.GetMinerManager(Config, _daemonClient); + var jobTracker = _objectFactory.GetJobTracker(); - var jobTracker = _objectFactory.GetJobTracker(); + var blockProcessor = _objectFactory.GetBlockProcessor(Config, _daemonClient); - _shareManager = _objectFactory.GetShareManager(Config, _daemonClient, jobTracker, storage); + _shareManager = _objectFactory.GetShareManager(Config, _daemonClient, jobTracker, storage, blockProcessor); - var vardiffManager = _objectFactory.GetVardiffManager(Config, _shareManager); + var vardiffManager = _objectFactory.GetVardiffManager(Config, _shareManager); - _banningManager = _objectFactory.GetBanManager(Config, _shareManager); + _banningManager = _objectFactory.GetBanManager(Config, _shareManager); - _jobManager = _objectFactory.GetJobManager(Config, _daemonClient, jobTracker, _shareManager, _minerManager, _hashAlgorithm); + _jobManager = _objectFactory.GetJobManager(Config, _daemonClient, jobTracker, _shareManager, _minerManager, _hashAlgorithm); + _jobManager.Initialize(InstanceId); - _jobManager.Initialize(InstanceId); + var paymentProcessor = _objectFactory.GetPaymentProcessor(Config, _daemonClient, storage, blockProcessor); + paymentProcessor.Initialize(Config.Payments); - var latestBlocks = _objectFactory.GetLatestBlocks(storage); - var blockStats = _objectFactory.GetBlockStats(latestBlocks, storage); - Statistics = _objectFactory.GetPerPoolStats(Config, _daemonClient, _minerManager, _hashAlgorithm, blockStats, storage); + var latestBlocks = _objectFactory.GetLatestBlocks(storage); + var blockStats = _objectFactory.GetBlockStats(latestBlocks, storage); + Statistics = _objectFactory.GetPerPoolStats(Config, _daemonClient, _minerManager, _hashAlgorithm, blockStats, storage); + } + catch (Exception e) + { + _logger.Error("Pool initialization error: {0:l}", e.Message); + } } private void InitServers() { + // todo: merge this with InitManagers so we don't have use private declaration of class instances + _servers = new Dictionary(); if (Config.Stratum != null && Config.Stratum.Enabled) diff --git a/src/CoiniumServ/Repository/Registries/ClassRegistry.cs b/src/CoiniumServ/Repository/Registries/ClassRegistry.cs index f8aa4261d..7af68b4af 100644 --- a/src/CoiniumServ/Repository/Registries/ClassRegistry.cs +++ b/src/CoiniumServ/Repository/Registries/ClassRegistry.cs @@ -21,6 +21,7 @@ // #endregion +using CoiniumServ.Blocks; using CoiniumServ.Coin.Config; using CoiniumServ.Configuration; using CoiniumServ.Daemon; @@ -55,12 +56,13 @@ public void RegisterInstances() _applicationContext.Container.Register().AsMultiInstance(); _applicationContext.Container.Register().AsMultiInstance(); _applicationContext.Container.Register().AsSingleton(); + _applicationContext.Container.Register().AsMultiInstance(); // statistics _applicationContext.Container.Register().AsSingleton(); _applicationContext.Container.Register().AsSingleton(); _applicationContext.Container.Register().AsMultiInstance(); - _applicationContext.Container.Register().AsMultiInstance(); + _applicationContext.Container.Register().AsMultiInstance(); _applicationContext.Container.Register().AsMultiInstance(); // config diff --git a/src/CoiniumServ/Shares/ShareManager.cs b/src/CoiniumServ/Shares/ShareManager.cs index 5237ecb3c..79c94413e 100644 --- a/src/CoiniumServ/Shares/ShareManager.cs +++ b/src/CoiniumServ/Shares/ShareManager.cs @@ -25,10 +25,11 @@ using System.Diagnostics; using System.Linq; using AustinHarris.JsonRpc; +using CoiniumServ.Blocks; using CoiniumServ.Daemon; +using CoiniumServ.Daemon.Responses; using CoiniumServ.Jobs.Tracker; using CoiniumServ.Persistance; -using CoiniumServ.Pools; using CoiniumServ.Pools.Config; using CoiniumServ.Server.Mining.Stratum; using CoiniumServ.Server.Mining.Stratum.Errors; @@ -50,6 +51,8 @@ public class ShareManager : IShareManager private readonly IStorage _storage; + private readonly IBlockProcessor _blockProcessor; + private readonly IPoolConfig _poolConfig; private readonly ILogger _logger; @@ -61,12 +64,14 @@ public class ShareManager : IShareManager /// /// /// - public ShareManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJobTracker jobTracker, IStorage storage) + /// + public ShareManager(IPoolConfig poolConfig, IDaemonClient daemonClient, IJobTracker jobTracker, IStorage storage, IBlockProcessor blockProcessor) { _poolConfig = poolConfig; _daemonClient = daemonClient; _jobTracker = jobTracker; _storage = storage; + _blockProcessor = blockProcessor; _logger = Log.ForContext().ForContext("Component", poolConfig.Coin.Name); } @@ -118,13 +123,19 @@ private void HandleValidShare(IShare share) // submit block candidate to daemon. var accepted = SubmitBlock(share); - // check if it was accepted and valid. - if (!accepted) - return; + // log about the acceptance status of the block. + _logger.Information( + accepted + ? "Found block [{0}] with hash: {1:l}" + : "Submitted block [{0}] with hash {1:l} but had to kick it as it was not accepted by the coin daemon", + share.Height, share.BlockHash.ToHexString()); + + if (!accepted) // if block wasn't accepted + return; // just return as we don't need to notify about it and store it. OnBlockFound(EventArgs.Empty); // notify the listeners about the new block. - _storage.AddBlock(share); // commit the block. + _storage.AddBlock(share); // commit the block details to storage. } private void HandleInvalidShare(IShare share) @@ -165,65 +176,39 @@ private void HandleInvalidShare(IShare share) private bool SubmitBlock(IShare share) { - // we should try different submission techniques and probably more then once: https://github.com/ahmedbodi/stratum-mining/blob/master/lib/bitcoin_rpc.py#L65-123 + // TODO: we should try different submission techniques and probably more then once: https://github.com/ahmedbodi/stratum-mining/blob/master/lib/bitcoin_rpc.py#L65-123 try { _daemonClient.SubmitBlock(share.BlockHex.ToHexString()); - var isAccepted = CheckBlock(share); - _logger.Information( - isAccepted - ? "Found block [{0}] with hash: {1:l}" - : "Submitted block [{0}] but got denied: {1:l}", - share.Height, share.BlockHash.ToHexString()); - - return isAccepted; - } - catch (Exception e) - { - _logger.Error(e, "Submit block failed - height: {0}, hash: {1:l}", share.Height, share.BlockHash); - return false; - } - } + // query the block against coin daemon and see if seems all good. + Block blockInfo; // the block repsonse from coin daemon. + Transaction genTx; // generation transaction response from coin daemon. + var exists = _blockProcessor.GetBlockDetails(share.BlockHash.ToHexString(), out blockInfo, out genTx); // query the coin daemon for the block details. - private bool CheckBlock(IShare share) - { - try - { - var block = _daemonClient.GetBlock(share.BlockHash.ToHexString()); // query the block. - - // calculate our generation transactions's hash - var genTxHash = share.CoinbaseHash.Bytes.ReverseBuffer().ToHexString(); - - // read the very first (generation transaction) of the block - var initialTx = block.Tx.First(); - - // make sure our calculated generation transaction hash matches the very first transaction reported for block by coin daemon. - if (genTxHash != initialTx) - { - Log.Debug("Kicked submitted block {0} because reported generation transaction hash [{1:l}] doesn't match our calculated one [{2:l}]", initialTx, genTxHash); + if (!exists) // make sure the block exists. return false; - } - - // also make sure the transaction includes our pool wallet address. - var transaction = _daemonClient.GetTransaction(initialTx); - // check if the transaction includes output for the configured central pool wallet address. - var gotPoolOutput = transaction.Details.Any(test => test.Address == _poolConfig.Wallet.Adress); + // calculate our expected generation transactions's hash + var expectedTxHash = share.CoinbaseHash.Bytes.ReverseBuffer().ToHexString(); - if (!gotPoolOutput) - { - Log.Debug("Kicked submitted block {0} because generation transaction doesn't contain an output for pool's central wallet address: {1:l]", share.Height, _poolConfig.Wallet.Adress); + // make sure our calculated and reported generation tx hashes match. + if (!_blockProcessor.CheckGenTxHash(blockInfo, expectedTxHash)) return false; - } + + // make sure the blocks generation transaction contains our central pool wallet address + if (!_blockProcessor.ContainsPoolOutput(genTx)) + return false; + + // if the code flows here, then it means the block was succesfully submitted and belongs to us. + share.SetFoundBlock(blockInfo); // assign the block to share. - share.SetFoundBlock(block); // assign the block to share. return true; } catch (Exception e) { - _logger.Error("Submitted block does not exist: {0}, hash: {1:l}, {2:l}", share.Height, share.BlockHash, e.Message); + _logger.Error(e, "Submit block failed - height: {0}, hash: {1:l} - {2:l}", share.Height, share.BlockHash, e.Message); return false; } } diff --git a/src/CoiniumServ/Statistics/Blocks.cs b/src/CoiniumServ/Statistics/BlocksCount.cs similarity index 94% rename from src/CoiniumServ/Statistics/Blocks.cs rename to src/CoiniumServ/Statistics/BlocksCount.cs index ad2795798..41e90d1de 100644 --- a/src/CoiniumServ/Statistics/Blocks.cs +++ b/src/CoiniumServ/Statistics/BlocksCount.cs @@ -25,7 +25,7 @@ namespace CoiniumServ.Statistics { - public class Blocks:IBlocks + public class BlocksCount:IBlocksCount { public int Pending { get; private set; } public int Confirmed { get; private set; } @@ -35,7 +35,7 @@ public class Blocks:IBlocks private readonly IStorage _storage; - public Blocks(ILatestBlocks latestBlocks, IStorage storage) + public BlocksCount(ILatestBlocks latestBlocks, IStorage storage) { _storage = storage; Latest = latestBlocks; diff --git a/src/CoiniumServ/Statistics/IBlocks.cs b/src/CoiniumServ/Statistics/IBlocksCount.cs similarity index 95% rename from src/CoiniumServ/Statistics/IBlocks.cs rename to src/CoiniumServ/Statistics/IBlocksCount.cs index 762d64ec0..649d3c1b4 100644 --- a/src/CoiniumServ/Statistics/IBlocks.cs +++ b/src/CoiniumServ/Statistics/IBlocksCount.cs @@ -23,7 +23,7 @@ namespace CoiniumServ.Statistics { - public interface IBlocks : IStatisticsProvider + public interface IBlocksCount : IStatisticsProvider { int Pending { get; } int Confirmed { get; } diff --git a/src/CoiniumServ/Statistics/IPerPool.cs b/src/CoiniumServ/Statistics/IPerPool.cs index 66387dce3..28798be5f 100644 --- a/src/CoiniumServ/Statistics/IPerPool.cs +++ b/src/CoiniumServ/Statistics/IPerPool.cs @@ -38,7 +38,7 @@ public interface IPerPool:IJsonResponse, IStatisticsProvider int CurrentBlock { get; } - IBlocks Blocks { get; } + IBlocksCount Blocks { get; } IPoolConfig Config { get; } } diff --git a/src/CoiniumServ/Statistics/PerPool.cs b/src/CoiniumServ/Statistics/PerPool.cs index e965f83fa..7defd9181 100644 --- a/src/CoiniumServ/Statistics/PerPool.cs +++ b/src/CoiniumServ/Statistics/PerPool.cs @@ -43,7 +43,7 @@ public class PerPool:IPerPool public int WorkerCount { get; private set; } public double Difficulty { get; private set; } public int CurrentBlock { get; private set; } - public IBlocks Blocks { get; private set; } + public IBlocksCount Blocks { get; private set; } public IPoolConfig Config { get; private set; } public string Json { get; private set; } @@ -57,7 +57,7 @@ public class PerPool:IPerPool private readonly double _shareMultiplier; - public PerPool(IPoolConfig poolConfig, IConfigManager configManager, IDaemonClient daemonClient,IMinerManager minerManager, IHashAlgorithm hashAlgorithm, IBlocks blockStatistics, IStorage storage) + public PerPool(IPoolConfig poolConfig, IConfigManager configManager, IDaemonClient daemonClient,IMinerManager minerManager, IHashAlgorithm hashAlgorithm, IBlocksCount blockStatistics, IStorage storage) { Config = poolConfig; _statisticsConfig = configManager.WebServerConfig.Statistics; diff --git a/src/CoiniumServ/web/default/pool.cshtml b/src/CoiniumServ/web/default/pool.cshtml index ac63c5b62..fa3c3f5d7 100644 --- a/src/CoiniumServ/web/default/pool.cshtml +++ b/src/CoiniumServ/web/default/pool.cshtml @@ -101,7 +101,6 @@ @block.Status break; case BlockStatus.Orphaned: - case BlockStatus.Kicked: @block.Status break; case BlockStatus.Confirmed: