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.CountSharesSoloBeforeCreatedAsync(con, tx, poolConfig.Id, block.Miner, shareCutOffDate.Value); if (cutOffCount > 0) { #if !DEBUG logger.Info(() => $"Deleting {cutOffCount} discarded shares for {block.Miner}"); await shareRepo.DeleteSharesSoloBeforeCreatedAsync(con, tx, poolConfig.Id, block.Miner, shareCutOffDate.Value); #endif } } }
public async Task UpdateBalancesAsync(IDbConnection con, IDbTransaction tx, PoolConfig poolConfig, IPayoutHandler payoutHandler, Block block, decimal blockReward) { var payoutConfig = poolConfig.PaymentProcessing.PayoutSchemeConfig; // POWRShare 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 = await CalculateRewards(poolConfig, window, block, blockReward, shares, rewards); // var updatedShares = PostAsync( // 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) { 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"); } }
public virtual async Task <decimal> UpdateBlockRewardBalancesAsync(IDbConnection con, IDbTransaction tx, Block block, PoolConfig pool) { var blockRewardRemaining = block.Reward; // Distribute funds to configured reward recipients foreach (var recipient in poolConfig.RewardRecipients.Where(x => x.Percentage > 0)) { var amount = block.Reward * (recipient.Percentage / 100.0m); var address = recipient.Address; blockRewardRemaining -= amount; // skip transfers from pool wallet to pool wallet if (address != poolConfig.Address) { logger.Info(() => $"Adding {FormatAmount(amount)} to balance of {address}"); await balanceRepo.AddAmountAsync(con, tx, poolConfig.Id, address, amount, $"Reward for block {block.BlockHeight}"); } } return(blockRewardRemaining); }
protected virtual async Task PersistPaymentsAsync(Balance[] balances, string transactionConfirmation) { var coin = poolConfig.Template.As <CoinTemplate>(); try { await faultPolicy.ExecuteAsync(async() => { await cf.RunTx(async(con, tx) => { foreach (var balance in balances) { if (!string.IsNullOrEmpty(transactionConfirmation) && !poolConfig.RewardRecipients.Any(x => x.Address == balance.Address)) { // record payment var payment = new Payment { PoolId = poolConfig.Id, Coin = coin.Symbol, Address = balance.Address, Amount = balance.Amount, Created = clock.Now, TransactionConfirmationData = transactionConfirmation }; await paymentRepo.InsertAsync(con, tx, payment); } // reset balance logger.Debug(() => $"[{LogCategory}] Resetting balance of {balance.Address}"); await balanceRepo.AddAmountAsync(con, tx, poolConfig.Id, coin.Symbol, balance.Address, -balance.Amount, $"Balance reset after payment"); } }); }); } catch (Exception ex) { logger.Error(ex, () => $"[{LogCategory}] Failed to persist the following payments: " + $"{JsonConvert.SerializeObject(balances.Where(x => x.Amount > 0).ToDictionary(x => x.Address, x => x.Amount))}"); throw; } }
public async Task UpdateBalancesAsync(IDbConnection con, IDbTransaction tx, IMiningPool pool, IPayoutHandler payoutHandler, Block block, decimal blockReward, CancellationToken ct) { var poolConfig = pool.Config; var shares = new Dictionary <string, double>(); var rewards = new Dictionary <string, decimal>(); var shareCutOffDate = await CalculateRewardsAsync(pool, payoutHandler, block, blockReward, shares, 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 {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, ct); if (cutOffCount > 0) { await LogDiscardedSharesAsync(ct, poolConfig, block, shareCutOffDate.Value); logger.Info(() => $"Deleting {cutOffCount} discarded shares before {shareCutOffDate.Value:O}"); await shareRepo.DeleteSharesBeforeCreatedAsync(con, tx, poolConfig.Id, shareCutOffDate.Value, ct); } } // 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"); } }
public async Task <object> AddMinerBalanceAsync(AddBalanceRequest request) { request.Usage = request.Usage?.Trim(); if (string.IsNullOrEmpty(request.Usage)) { request.Usage = $"Баланс администратора был изменён с адреса {Request.HttpContext.Connection.RemoteIpAddress}"; } var oldBalance = await cf.Run(con => balanceRepo.GetBalanceAsync(con, request.PoolId, request.Address)); var count = await cf.RunTx(async (con, tx) => { return(await balanceRepo.AddAmountAsync(con, tx, request.PoolId, request.Address, request.Amount, request.Usage)); }); var newBalance = await cf.Run(con => balanceRepo.GetBalanceAsync(con, request.PoolId, request.Address)); return(new { oldBalance, newBalance }); }