Пример #1
0
        private async Task CalculateBlockEffort(PoolConfig pool, Block block, IPayoutHandler handler)
        {
            // get share date-range
            var from = DateTime.MinValue;
            var to   = block.Created;

            // get last block for pool
            var lastBlock = cf.Run(con => blockRepo.GetBlockBefore(con, pool.Id, new[]
            {
                BlockStatus.Confirmed,
                BlockStatus.Orphaned,
                BlockStatus.Pending,
            }, block.Created));

            if (lastBlock != null)
            {
                from = lastBlock.Created;
            }

            // get combined diff of all shares for block
            var accumulatedShareDiffForBlock = cf.Run(con =>
                                                      shareRepo.GetAccumulatedShareDifficultyBetweenCreated(con, pool.Id, from, to));

            // handler has the final say
            if (accumulatedShareDiffForBlock.HasValue)
            {
                await handler.CalculateBlockEffortAsync(block, accumulatedShareDiffForBlock.Value);
            }
        }
Пример #2
0
        private async Task CalculateBlockEffort(PoolConfig pool, Block block, IPayoutHandler handler)
        {
            var from = DateTime.MinValue;
            var to   = block.Created;

            var lastBlock = cf.Run(con => blockRepo.GetBlockBefore(con, pool.Id, new[]
            {
                BlockStatus.Confirmed,
                BlockStatus.Orphaned,
                BlockStatus.Pending,
            }, block.Created));

            if (lastBlock != null)
            {
                from = lastBlock.Created;
            }

            var accumulatedShareDiffForBlock = cf.Run(con =>
                                                      shareRepo.GetAccumulatedShareDifficultyBetweenCreated(con, pool.Id, from, to));

            if (accumulatedShareDiffForBlock.HasValue)
            {
                await handler.CalculateBlockEffortAsync(block, accumulatedShareDiffForBlock.Value);
            }
        }
Пример #3
0
        private async Task PayoutPoolBalancesAsync(PoolConfig pool, IPayoutHandler handler)
        {
            var poolBalancesOverMinimum = cf.Run(con =>
                                                 balanceRepo.GetPoolBalancesOverThreshold(con, pool.Id, pool.PaymentProcessing.MinimumPayment));

            if (poolBalancesOverMinimum.Length > 0)
            {
                try
                {
                    await handler.PayoutAsync(poolBalancesOverMinimum);
                }

                catch (Exception ex)
                {
                    await NotifyPayoutFailureAsync(poolBalancesOverMinimum, pool, ex);

                    throw;
                }
            }

            else
            {
                logger.Info(() => $"No balances over configured minimum payout for pool {pool.Id}");
            }
        }
Пример #4
0
        public async Task UpdateBalancesAsync(IDbConnection con, IDbTransaction tx, PoolConfig poolConfig,
                                              IPayoutHandler payoutHandler, Block block, decimal blockReward)
        {
            // calculate rewards
            var rewards         = new Dictionary <string, decimal>();
            var shareCutOffDate = CalculateRewards(poolConfig, block, blockReward, rewards);

            // update balances
            foreach (var address in rewards.Keys)
            {
                var amount = rewards[address];

                if (amount > 0)
                {
                    logger.Info(() => $"Adding {payoutHandler.FormatAmount(amount)} to balance of {address} for block {block.BlockHeight}");
                    await balanceRepo.AddAmountAsync(con, tx, poolConfig.Id, address, amount, $"Reward for block {block.BlockHeight}");
                }
            }

            // delete discarded shares
            if (shareCutOffDate.HasValue)
            {
                var cutOffCount = await shareRepo.CountSharesBeforeCreatedAsync(con, tx, poolConfig.Id, shareCutOffDate.Value);

                if (cutOffCount > 0)
                {
#if !DEBUG
                    logger.Info(() => $"Deleting {cutOffCount} discarded shares before {shareCutOffDate.Value:O}");
                    await shareRepo.DeleteSharesBeforeCreatedAsync(con, tx, poolConfig.Id, shareCutOffDate.Value);
#endif
                }
            }
        }
Пример #5
0
        private async Task UpdatePoolBalancesAsync(PoolConfig pool, IPayoutHandler handler, IPayoutScheme scheme)
        {
            // get pending blockRepo for pool
            var pendingBlocks = await cf.Run(con => blockRepo.GetPendingBlocksForPoolAsync(con, pool.Id));

            // classify
            var updatedBlocks = await handler.ClassifyBlocksAsync(pendingBlocks);

            if (updatedBlocks.Any())
            {
                foreach (var block in updatedBlocks.OrderBy(x => x.Created))
                {
                    logger.Info(() => $"Processing payments for pool {pool.Id}, block {block.BlockHeight}, effort {block.Effort}");


                    await cf.RunTx(async (con, tx) =>
                    {
                        if (!block.Effort.HasValue)  // fill block effort if empty
                        {
                            await CalculateBlockEffortAsync(pool, block, handler);
                        }

                        switch (block.Status)
                        {
                        case BlockStatus.Confirmed:
                            // blockchains that do not support block-reward payments via coinbase Tx
                            // must generate balance records for all reward recipients instead
                            var blockReward = await handler.UpdateBlockRewardBalancesAsync(con, tx, block, pool);

                            logger.Info(() => $" --Pool {pool}");
                            logger.Info(() => $" --Block {block}");
                            logger.Info(() => $" --Block reward {blockReward}");

                            logger.Info(() => $" --Con {con}");
                            logger.Info(() => $" --tx {tx}");

                            await scheme.UpdateBalancesAsync(con, tx, pool, handler, block, blockReward);
                            await blockRepo.UpdateBlockAsync(con, tx, block);
                            break;

                        case BlockStatus.Orphaned:
                            await blockRepo.UpdateBlockAsync(con, tx, block);
                            break;

                        case BlockStatus.Pending:
                            await blockRepo.UpdateBlockAsync(con, tx, block);
                            break;
                        }
                    });
                }
            }

            else
            {
                logger.Info(() => $"No updated blocks for pool {pool.Id}");
            }
        }
Пример #6
0
        private async Task UpdatePoolBalancesAsync(PoolConfig pool, IPayoutHandler handler, IPayoutScheme scheme)
        {
            // get pending blockRepo for pool
            var pendingBlocks = cf.Run(con => blockRepo.GetPendingBlocksForPool(con, pool.Id));

            // classify
            var updatedBlocks = await handler.ClassifyBlocksAsync(pendingBlocks);

            if (updatedBlocks.Any())
            {
                foreach (var block in updatedBlocks.OrderBy(x => x.Created))
                {
                    logger.Info(() => $"Processing payments for pool {pool.Id}, block {block.BlockHeight}");

                    await cf.RunTxAsync(async (con, tx) =>
                    {
                        // fill block effort if empty
                        if (!block.Effort.HasValue)
                        {
                            await CalculateBlockEffort(pool, block, handler);
                        }

                        switch (block.Status)
                        {
                        case BlockStatus.Confirmed:
                            // blockchains that do not support block-reward payments via coinbase Tx
                            // must generate balance records for all reward recipients instead
                            var blockReward = await handler.UpdateBlockRewardBalancesAsync(con, tx, block, pool);

                            // update share submitter balances through configured payout scheme
                            await scheme.UpdateBalancesAsync(con, tx, pool, handler, block, blockReward);

                            // finally update block status
                            blockRepo.UpdateBlock(con, tx, block);
                            break;

                        case BlockStatus.Orphaned:
                            logger.Info(() => $"Deleting orphaned block {block.BlockHeight} on pool {pool.Id}");

                            blockRepo.DeleteBlock(con, tx, block);
                            break;

                        case BlockStatus.Pending:
                            blockRepo.UpdateBlock(con, tx, block);
                            break;
                        }
                    });
                }
            }

            else
            {
                logger.Info(() => $"No updated blocks for {pool.Id}");
            }
        }
 public ProductsMonitorService(IDateTimeNow dateTimeNow)
 {
     _dateTimeNow = dateTimeNow;
     _modelPortfolioRepository = new ModelPortfolioRepository();
     _behaviourFactory         = new BehaviourFactory();
     _productRepository        = new ProductRepository(_modelPortfolioRepository, _behaviourFactory, _dateTimeNow);
     _rebalancerHandler        = new RebalanceHandler(_behaviourFactory);
     _payoutHandler            = new PayoutHandler(_behaviourFactory);
     _monitorFactory           = new MonitorFactory(_dateTimeNow, _rebalancerHandler, _payoutHandler);
     _monitorHandler           = new MonitorHandler(_monitorFactory);
 }
        public Task UpdateBalancesAsync(IDbConnection con, IDbTransaction tx, PoolConfig poolConfig,
                                        IPayoutHandler payoutHandler, Block block, decimal blockReward)
        {
            var payoutConfig = poolConfig.PaymentProcessing.PayoutSchemeConfig;

            // PPLNS window (see https://bitcointalk.org/index.php?topic=39832)
            var window = payoutConfig?.ToObject <Config>()?.Factor ?? 2.0m;

            // calculate rewards
            var shares          = new Dictionary <string, double>();
            var rewards         = new Dictionary <string, decimal>();
            var shareCutOffDate = CalculateRewards(poolConfig, window, block, blockReward, shares, rewards);

            // update balances
            foreach (var address in rewards.Keys)
            {
                var amount = rewards[address];

                if (amount > 0)
                {
                    logger.Info(() => $"Adding {payoutHandler.FormatAmount(amount)} to balance of {address} for {FormatUtil.FormatQuantity(shares[address])} ({shares[address]}) shares for block {block.BlockHeight}");
                    balanceRepo.AddAmount(con, tx, poolConfig.Id, poolConfig.Coin.Type, address, amount, $"Reward for {FormatUtil.FormatQuantity(shares[address])} shares for block {block.BlockHeight}");
                }
            }

            // delete discarded shares
            if (shareCutOffDate.HasValue)
            {
                var cutOffCount = shareRepo.CountSharesBeforeCreated(con, tx, poolConfig.Id, shareCutOffDate.Value);

                if (cutOffCount > 0)
                {
                    LogDiscardedShares(poolConfig, block, shareCutOffDate.Value);

#if !DEBUG
                    logger.Info(() => $"Deleting {cutOffCount} discarded shares before {shareCutOffDate.Value:O}");
                    shareRepo.DeleteSharesBeforeCreated(con, tx, poolConfig.Id, shareCutOffDate.Value);
#endif
                }
            }

            // diagnostics
            var totalShareCount = shares.Values.ToList().Sum(x => new decimal(x));
            var totalRewards    = rewards.Values.ToList().Sum(x => x);

            if (totalRewards > 0)
            {
                logger.Info(() => $"{FormatUtil.FormatQuantity((double) totalShareCount)} ({Math.Round(totalShareCount, 2)}) shares contributed to a total payout of {payoutHandler.FormatAmount(totalRewards)} ({totalRewards / blockReward * 100:0.00}% of block reward) to {rewards.Keys.Count} addresses");
            }

            return(Task.FromResult(true));
        }
Пример #9
0
        public Task UpdateBalancesAsync(IDbConnection con, IDbTransaction tx, PoolConfig poolConfig,
                                        IPayoutHandler payoutHandler, Block block, decimal blockReward)
        {
            var payoutConfig = poolConfig.PaymentProcessing.PayoutSchemeConfig;

            // PPLNS window (see https://bitcointalk.org/index.php?topic=39832)
            var window = payoutConfig?.ToObject <Config>()?.Factor ?? 2.0m;

            // calculate rewards
            var shares          = new Dictionary <string, ulong>();
            var rewards         = new Dictionary <string, decimal>();
            var shareCutOffDate = CalculateRewards(poolConfig, window, block, blockReward, shares, rewards);

            // update balances
            foreach (var address in rewards.Keys)
            {
                var amount = rewards[address];

                if (amount > 0)
                {
                    logger.Info(() => $"Adding {payoutHandler.FormatAmount(amount)} to balance of {address} for {shares[address]} shares");
                    balanceRepo.AddAmount(con, tx, poolConfig.Id, poolConfig.Coin.Type, address, amount);
                }
            }

            // delete obsolete shares
            if (shareCutOffDate.HasValue)
            {
                var cutOffCount = shareRepo.CountPoolSharesBefore(con, tx, poolConfig.Id, shareCutOffDate.Value);

                if (cutOffCount > 0)
                {
                    logger.Info(() => $"Deleting {cutOffCount} obsolete shares before {shareCutOffDate.Value}");
                    shareRepo.DeletePoolSharesBefore(con, tx, poolConfig.Id, shareCutOffDate.Value);
                }

                //logger.Info(() => $"Shares before {shareCutOffDate.Value} can be deleted");
            }

            // diagnostics
            var totalShareCount = shares.Values.ToList().Sum(x => new decimal(x));
            var totalRewards    = rewards.Values.ToList().Sum(x => x);

            if (totalRewards > 0)
            {
                logger.Info(() => $"{totalShareCount} shares contributed to a total payout of {payoutHandler.FormatAmount(totalRewards)} ({totalRewards / blockReward * 100:0.00}% of block reward)");
            }

            return(Task.FromResult(true));
        }
Пример #10
0
        public async Task UpdateBalancesAsync(IDbConnection con, IDbTransaction tx, PoolConfig poolConfig,
                                              IPayoutHandler payoutHandler, Block block, decimal blockReward)
        {
            var payoutConfig    = poolConfig.PaymentProcessing.PayoutSchemeConfig;
            var window          = payoutConfig?.ToObject <Config>()?.Factor ?? 2.0m;
            var shares          = new Dictionary <string, double>();
            var rewards         = new Dictionary <string, decimal>();
            var shareCutOffDate = await CalculateRewardsAsync(poolConfig, block, blockReward, shares, rewards);

            // update balances
            foreach (var address in rewards.Keys)
            {
                var amount = rewards[address];

                if (amount > 0)
                {
                    logger.Info(() => $"Adding {payoutHandler.FormatAmount(amount)} to balance of {address} for {FormatUtil.FormatQuantity(shares[address])} ({shares[address]}) shares for block {block.BlockHeight}");
                    await balanceRepo.AddAmountAsync(con, tx, poolConfig.Id, address, amount, $"Reward for {FormatUtil.FormatQuantity(shares[address])} shares for block {block.BlockHeight}");
                }
            }

            // delete discarded shares
            if (shareCutOffDate.HasValue)
            {
                var cutOffCount = await shareRepo.CountSharesBeforeCreatedAsync(con, tx, poolConfig.Id, shareCutOffDate.Value);

                if (cutOffCount > 0)
                {
                    await LogDiscardedSharesAsync(poolConfig, block, shareCutOffDate.Value);

                    logger.Info(() => $"Deleting {cutOffCount} discarded shares before {shareCutOffDate.Value:O}");
                    await shareRepo.DeleteSharesBeforeCreatedAsync(con, tx, poolConfig.Id, shareCutOffDate.Value);
                }
            }

            // diagnostics
            var totalShareCount = shares.Values.ToList().Sum(x => new decimal(x));
            var totalRewards    = rewards.Values.ToList().Sum(x => x);

            if (totalRewards > 0)
            {
                logger.Info(() => $"{FormatUtil.FormatQuantity((double) totalShareCount)} ({Math.Round(totalShareCount, 2)}) shares contributed to a total payout of {payoutHandler.FormatAmount(totalRewards)} ({totalRewards / blockReward * 100:0.00}% of block reward) to {rewards.Keys.Count} addresses");
            }
        }
Пример #11
0
        private async Task UpdatePoolBalancesAsync(PoolConfig pool, IPayoutHandler handler, IPayoutScheme scheme)
        {
            var pendingBlocks = cf.Run(con => blockRepo.GetPendingBlocksForPool(con, pool.Id));

            var updatedBlocks = await handler.ClassifyBlocksAsync(pendingBlocks);

            if (updatedBlocks.Any())
            {
                foreach (var block in updatedBlocks.OrderBy(x => x.Created))
                {
                    logger.Info(() => $"Processing payments for pool {pool.Id}, block {block.BlockHeight}");

                    await cf.RunTxAsync(async (con, tx) =>
                    {
                        if (!block.Effort.HasValue)
                        {
                            await CalculateBlockEffort(pool, block, handler);
                        }

                        switch (block.Status)
                        {
                        case BlockStatus.Confirmed:
                            var blockReward = await handler.UpdateBlockRewardBalancesAsync(con, tx, block, pool);

                            await scheme.UpdateBalancesAsync(con, tx, pool, handler, block, blockReward);

                            blockRepo.UpdateBlock(con, tx, block);
                            break;

                        case BlockStatus.Orphaned:
                        case BlockStatus.Pending:
                            blockRepo.UpdateBlock(con, tx, block);
                            break;
                        }
                    });
                }
            }

            else
            {
                logger.Info(() => $"No updated blocks for pool {pool.Id}");
            }
        }
Пример #12
0
    public async Task UpdateBalancesAsync(IDbConnection con, IDbTransaction tx, IMiningPool pool, IPayoutHandler payoutHandler,
                                          Block block, decimal blockReward, CancellationToken ct)
    {
        var poolConfig = pool.Config;

        // calculate rewards
        var rewards         = new Dictionary <string, decimal>();
        var shareCutOffDate = CalculateRewards(block, blockReward, rewards, ct);

        // update balances
        foreach (var address in rewards.Keys)
        {
            var amount = rewards[address];

            if (amount > 0)
            {
                logger.Info(() => $"Adding {payoutHandler.FormatAmount(amount)} to balance of {address} for block {block.BlockHeight}");

                await balanceRepo.AddAmountAsync(con, tx, poolConfig.Id, address, amount, $"Reward for block {block.BlockHeight}");
            }
        }

        // delete discarded shares
        if (shareCutOffDate.HasValue)
        {
            var cutOffCount = await shareRepo.CountSharesByMinerAsync(con, tx, poolConfig.Id, block.Miner, ct);

            if (cutOffCount > 0)
            {
                logger.Info(() => $"Deleting {cutOffCount} discarded shares for {block.Miner}");

                await shareRepo.DeleteSharesByMinerAsync(con, tx, poolConfig.Id, block.Miner, ct);
            }
        }
    }
Пример #13
0
 public MonitorFactory(IDateTimeNow dateTimeNow, IRebalanceHandler rebalancerHandler, IPayoutHandler payoutHandler)
 {
     _dateTimeNow       = dateTimeNow;
     _rebalancerHandler = rebalancerHandler;
     _payoutHandler     = payoutHandler;
 }
Пример #14
0
 public PayoutMonitor(IRebalanceHandler rebalancerHandler, IPayoutHandler payoutHandler, ProductBase productBase, IDateTimeNow now) : base(productBase, now)
 {
     _rebalancerHandler = rebalancerHandler;
     _payoutHandler     = payoutHandler;
 }