public async Task returns_unclaimed_units_correctly() { var depositId = Keccak.Zero; TestConsumer consumer = TestConsumer.ForDeposit(depositId) .WithNode(1).AddSession().WithUnpaidUnits(10) .And.WithNode(2).AddSession().WithUnpaidUnits(20) .And.Build(); ConfigureMocks(consumer); _sessionManager.GetSession(depositId, consumer.Node(1).Node.Peer) .Returns(consumer.Node(1).Node.Sessions.First(s => s.DepositId == depositId)); IDepositNodesHandler depositHandler = await _depositManager.InitAsync(depositId); AddReciptsToMerge(consumer, depositHandler); _receiptsPolicies.CanMergeReceipts(depositHandler.UnmergedUnits, depositHandler.UnitPrice).Returns(true); depositHandler.SetConsumedUnits(80); depositHandler.SetUnmergedUnits(50); await _depositManager.HandleUnpaidUnitsAsync(depositId, consumer.Node(1).Node.Peer); var unclaimedUnits = _depositManager.GetUnclaimedUnits(depositId); Assert.AreEqual(10, unclaimedUnits); }
public void Setup() { _depositNodesHandlerFactory = Substitute.For <IDepositNodesHandlerFactory>(); _sessionManager = Substitute.For <ISessionManager>(); _receiptsPolicies = Substitute.For <IReceiptsPolicies>(); _receiptProcessor = Substitute.For <IReceiptProcessor>(); _paymentClaimProcessor = Substitute.For <IPaymentClaimProcessor>(); _consumerRepository = Substitute.For <IConsumerRepository>(); _paymentClaimRepository = Substitute.For <IPaymentClaimRepository>(); _sessionRepository = Substitute.For <IProviderSessionRepository>(); _sessionRepository.BrowseAsync(Arg.Any <GetProviderSessions>()).Returns(PagedResult <ProviderSession> .Empty); _receiptRepository = Substitute.For <IReceiptRepository>(); var unixTime = UnixTime.FromSeconds(100); _timestamper = Substitute.For <ITimestamper>(); _timestamper.UnixTime.Returns(unixTime); _gasPriceService = Substitute.For <IGasPriceService>(); _logManager = Substitute.For <ILogManager>(); _wallet = Substitute.For <IWallet>(); _address = Address.Zero; _consumer = Address.Zero; _depositNodesHandler = new InMemoryDepositNodesHandler(Keccak.Zero, _consumer, DataAssetUnitType.Unit, 0, 100, 0, 0, 0, 0, 0, 0, 0, null, null, 0); _depositNodesHandlerFactory.CreateInMemory(Arg.Any <Keccak>(), _consumer, Arg.Any <DataAssetUnitType>(), Arg.Any <uint>(), Arg.Any <uint>(), Arg.Any <UInt256>(), Arg.Any <uint>(), Arg.Any <uint>(), Arg.Any <uint>(), Arg.Any <uint>(), Arg.Any <uint>(), Arg.Any <uint>(), Arg.Any <PaymentClaim>(), Arg.Any <IEnumerable <DataDeliveryReceiptDetails> >(), Arg.Any <uint>()).Returns(_depositNodesHandler); _depositManager = new DepositManager(_depositNodesHandlerFactory, _sessionManager, _receiptsPolicies, _wallet, _address, _receiptProcessor, _paymentClaimProcessor, _consumerRepository, _paymentClaimRepository, _receiptRepository, _sessionRepository, _timestamper, _gasPriceService, _logManager); }
// There is no need to match merge threshold while all of the units has been consumed public async Task will_merge_if_consumed_all_but_did_not_match_threshold() { var depositId = Keccak.Zero; TestConsumer consumer = TestConsumer.ForDeposit(depositId) .WithNode(1).AddSession().WithUnpaidUnits(10) .And.WithNode(2).AddSession().WithUnpaidUnits(20) .And.Build(); ConfigureMocks(consumer); _sessionManager.GetSession(depositId, consumer.Node(1).Node.Peer) .Returns(consumer.Node(1).Node.Sessions.First(s => s.DepositId == depositId)); IDepositNodesHandler depositHandler = await _depositManager.InitAsync(depositId); AddReciptsToMerge(consumer, depositHandler); _receiptsPolicies.CanMergeReceipts(depositHandler.UnmergedUnits, depositHandler.UnitPrice).Returns(false); depositHandler.SetConsumedUnits(100); depositHandler.SetUnmergedUnits(50); await _depositManager.HandleUnpaidUnitsAsync(depositId, consumer.Node(1).Node.Peer); Assert.IsTrue(depositHandler.UnmergedUnits == 0); }
private async Task TryClaimPaymentAsync(IDepositNodesHandler deposit) { if (_accountLocked) { if (_logger.IsWarn) { _logger.Warn($"Account: '{_providerAddress}' is locked, can't claim a payment."); } return; } if (!deposit.Receipts.Any()) { return; } if (deposit.HasClaimedAllUnits) { if (_logger.IsInfo) { _logger.Info($"Last payment was already claimed for deposit: '{deposit.DepositId}'."); } return; } try { var unclaimedUnits = GetUnclaimedUnits(deposit); if (deposit.ConsumedAll || await _receiptsPolicies.CanClaimPayment(unclaimedUnits, deposit.UnitPrice)) { UInt256 gasPrice = await _gasPriceService.GetCurrentGasPriceAsync(); UInt256 fee = gasPrice * GasLimit; UInt256 profit = unclaimedUnits * deposit.UnitPrice; if (fee >= profit) { if (_logger.IsWarn) { _logger.Warn($"Claiming a payment would cause loss (fee: {fee} wei >= profit: {profit} wei)."); } return; } await ClaimPaymentAsync(deposit); } } catch (Exception ex) { if (_logger.IsWarn) { _logger.Warn($"There was an error when claiming a payment for deposit: '{deposit.DepositId}'. {ex}"); } } }
private uint GetUnclaimedUnits(IDepositNodesHandler deposit) { var latestReceiptUnitsRange = deposit.LatestReceipt?.IsClaimed == true ? null : deposit.LatestReceipt?.Request.UnitsRange; var unclaimedRange = deposit.LatestMergedReceipt?.IsClaimed == true ? latestReceiptUnitsRange : deposit.LatestMergedReceipt?.Request.UnitsRange; return(unclaimedRange is null ? 0 : unclaimedRange.To - unclaimedRange.From + 1); }
public async Task will_calculate_units_correctly_when_unit_type_is_time() { var depositId = Keccak.Zero; TestConsumer consumer = TestConsumer.ForDeposit(depositId, DataAssetUnitType.Time) .WithNode(1).AddSession().WithUnpaidUnits(10).WithConsumedUnits(30) .And.Build(); ConfigureMocks(consumer); var depositNodesHandler = new InMemoryDepositNodesHandler(Keccak.Zero, _consumer, DataAssetUnitType.Time, 1, 100, 1, 60, 50, 30, 50, 0, 0, null, null, 0); _depositNodesHandlerFactory.CreateInMemory(depositId, Arg.Any <Address>(), DataAssetUnitType.Time, Arg.Any <uint>(), Arg.Any <uint>(), Arg.Any <UInt256>(), Arg.Any <uint>(), Arg.Any <uint>(), Arg.Any <uint>(), Arg.Any <uint>(), Arg.Any <uint>(), Arg.Any <uint>(), Arg.Any <PaymentClaim>(), Arg.Any <IEnumerable <DataDeliveryReceiptDetails> >(), Arg.Any <uint>()) .Returns(depositNodesHandler); IDepositNodesHandler depositHandler = await _depositManager.InitAsync(depositId); await _depositManager.HandleConsumedUnitAsync(depositId); Assert.IsTrue(depositHandler.ConsumedUnits == 99); Assert.IsTrue(depositHandler.UnpaidUnits == 79); Assert.IsTrue(depositHandler.UnclaimedUnits == 89); Assert.IsTrue(depositHandler.UnmergedUnits == 69); }
private void AddReciptsToMerge(TestConsumer consumer, IDepositNodesHandler depositHandler) { DataDeliveryReceiptRequest request = new DataDeliveryReceiptRequest(1, consumer.DepositId, new UnitsRange(0, 5), false, new List <DataDeliveryReceiptToMerge> { new DataDeliveryReceiptToMerge(new UnitsRange(0, 1), new Signature(1, 2, 37)) }); DataDeliveryReceipt receipt = new DataDeliveryReceipt(StatusCodes.Ok, 50, 0, new Signature(1, 2, 37)); DataDeliveryReceiptDetails receiptDetails = new DataDeliveryReceiptDetails(Keccak.OfAnEmptyString, consumer.Sessions.First().Id, consumer.DataAsset.Id, null, request, receipt, 10, true); depositHandler.AddReceipt(receiptDetails); DataDeliveryReceiptRequest request2 = new DataDeliveryReceiptRequest(1, consumer.DepositId, new UnitsRange(6, 49)); DataDeliveryReceipt receipt2 = new DataDeliveryReceipt(StatusCodes.Ok, 50, 0, new Signature(1, 2, 37)); DataDeliveryReceiptDetails receiptDetails2 = new DataDeliveryReceiptDetails(Keccak.OfAnEmptyString, consumer.Sessions.First().Id, consumer.DataAsset.Id, null, request2, receipt2, 10, false); depositHandler.AddReceipt(receiptDetails2); }
// While running HandleUnpaidUnitsAsync() DepositManager will try to claim payment for // unpaid units. // The method starts with previous session and checks whether there are any unpaid units and then // moves to the current one. public async Task can_handle_unpaid_units() { var depositId = Keccak.Zero; TestConsumer consumer = TestConsumer.ForDeposit(depositId) .WithNode(1).AddSession().WithUnpaidUnits(10) .And.WithNode(2).AddSession().WithUnpaidUnits(20) .And.Build(); ConfigureMocks(consumer); _sessionManager.GetSession(depositId, consumer.Node(1).Node.Peer) .Returns(consumer.Node(1).Node.Sessions.First(s => s.DepositId == depositId)); IDepositNodesHandler depositHandler = await _depositManager.InitAsync(depositId); await _depositManager.HandleUnpaidUnitsAsync(depositId, consumer.Node(1).Node.Peer); Assert.IsTrue(depositHandler.UnpaidUnits == 0); }
private async Task <(DataDeliveryReceipt, RequestReceiptStatus)> TryHandleDeliveryReceipt(IDepositNodesHandler deposit, Address consumer, ProviderSession session, DataDeliveryReceiptRequest request, INdmProviderPeer peer, bool isRetry = false) { while (true) { Keccak depositId = session.DepositId; DataDeliveryReceipt receipt = await peer.SendRequestDataDeliveryReceiptAsync(request, CancellationToken.None); if (_logger.IsInfo) { _logger.Info($"Received a delivery receipt for deposit: '{depositId}' from node: '{peer.NodeId}', status code: '{receipt.StatusCode}'."); } if (isRetry && receipt.StatusCode != StatusCodes.Ok) { if (_logger.IsWarn) { _logger.Warn($"Request receipt retry failed for: '{depositId}' from node: '{peer.NodeId}', status code: '{receipt.StatusCode}'."); } return(receipt, RequestReceiptStatus.Invalid); } switch (receipt.StatusCode) { case StatusCodes.Ok: if (await _receiptProcessor.TryProcessAsync(session, consumer, peer, request, receipt)) { return(receipt, RequestReceiptStatus.Ok); } return(receipt, RequestReceiptStatus.Invalid); case StatusCodes.InvalidReceiptRequestRange: { if (_logger.IsInfo) { _logger.Info($"Consumer for deposit: '{depositId}' from node: '{peer.NodeId}' claims {receipt.ConsumedUnits} consumed and {receipt.UnpaidUnits} unpaid units."); } UnitsRange range = request.UnitsRange; uint requestedUnits = range.To - range.From + 1; if (requestedUnits <= receipt.UnpaidUnits) { if (_logger.IsWarn) { _logger.Warn($"Consumer for deposit: '{depositId}' from node: '{peer.NodeId}' claimed an invalid range (while it was actually valid)."); } break; } uint graceUnits = requestedUnits - receipt.UnpaidUnits; uint totalGraceUnits = graceUnits + deposit.GraceUnits; bool hasReachedThreshold = await _receiptsPolicies.CanClaimPayment(totalGraceUnits, deposit.UnitPrice); if (hasReachedThreshold) { peer.SendGraceUnitsExceeded(depositId, deposit.ConsumedUnits, totalGraceUnits); if (_logger.IsWarn) { _logger.Warn($"Consumer for deposit: '{depositId}' from node: '{peer.NodeId}' claimed too many unpaid units, grace units exceeded ({totalGraceUnits})."); } return(receipt, RequestReceiptStatus.GraceUnitsExceeded); } if (_logger.IsInfo) { _logger.Info($"Unpaid units difference is: {graceUnits} ({requestedUnits} - {receipt.UnpaidUnits}). Lowering units amount for deposit: '{depositId}'."); } deposit.SubtractUnpaidUnits(graceUnits); deposit.SubtractUnmergedUnits(graceUnits); deposit.SubtractUnclaimedUnits(graceUnits); deposit.AddGraceUnits(graceUnits); session.SubtractUnpaidUnits(graceUnits); session.AddGraceUnits(graceUnits); if (range.To < graceUnits) { peer.SendGraceUnitsExceeded(depositId, deposit.ConsumedUnits, totalGraceUnits); if (_logger.IsWarn) { _logger.Warn($"Cannot request a receipt for deposit: '{depositId}' - grace units amount is greater than the receipt range ({graceUnits} > {range.To})."); } return(receipt, RequestReceiptStatus.GraceUnitsExceeded); } uint updatedRangeTo = range.To - graceUnits; if (range.From > updatedRangeTo) { if (_logger.IsWarn) { _logger.Warn($"Invalid updated range [{range.From}, {updatedRangeTo}] for: '{depositId}' - receipt request will not be send."); } return(receipt, RequestReceiptStatus.Invalid); } if (_logger.IsInfo) { _logger.Info($"Grace units for deposit: '{depositId}' is: {deposit.GraceUnits} (added {graceUnits})."); } UnitsRange updatedRange = new UnitsRange(range.From, updatedRangeTo); if (_logger.IsInfo) { _logger.Info($"Updated range for deposit: '{depositId}' [{updatedRange.From}, {updatedRange.To}]. Requesting receipt once again."); } request = request.WithRange(updatedRange, deposit.GetNextReceiptRequestNumber()); isRetry = true; continue; } case StatusCodes.InvalidReceiptAddress: { break; } case StatusCodes.Error: { break; } } return(receipt, RequestReceiptStatus.Invalid); } }
private DataDeliveryReceiptRequest?CreateMergedRequest(IDepositNodesHandler deposit) { if (deposit.LatestReceipt == null) { return(null); } if (_logger.IsInfo) { _logger.Info($"Creating merged receipt request for deposit: '{deposit.DepositId}'."); } bool isLastReceipt = deposit.LatestReceipt.Request.UnitsRange.To == deposit.PurchasedUnits - 1; if (isLastReceipt && _logger.IsInfo) { _logger.Info($"Merged receipt request for deposit: '{deposit.DepositId}' will be the last one."); } uint latestPaymentClaimRangeTo = deposit.LatestPaymentClaim?.UnitsRange.To ?? 0; DataDeliveryReceiptDetails?latestMergedReceipt = deposit.Receipts.OrderBy(r => r.Timestamp) .ThenBy(r => r.Request.UnitsRange.To) .LastOrDefault(r => r.IsMerged); uint latestMergedReceiptRangeFrom = latestMergedReceipt?.Request.UnitsRange.From ?? 0; long latestMergedReceiptRangeTo = latestMergedReceipt?.Request.UnitsRange.To ?? 0; var mergeableReceipts = deposit.Receipts .Where(r => r.Timestamp >= (latestMergedReceipt?.Timestamp ?? 0) && r.Request.UnitsRange.To > (latestMergedReceipt is null ? -1 : latestMergedReceiptRangeTo)) .OrderBy(r => r.Timestamp) .ThenBy(r => r.Request.UnitsRange.To) .ToList(); var paymentClaimExist = deposit.LatestPaymentClaim is { }; uint rangeFrom; if (paymentClaimExist) { rangeFrom = latestPaymentClaimRangeTo + 1; } else { if (latestMergedReceiptRangeFrom > 0) { rangeFrom = latestMergedReceiptRangeFrom + 1; } else { rangeFrom = 0; } } uint rangeTo = deposit.LatestReceipt.Request.UnitsRange.To; if (rangeFrom > rangeTo) { return(null); } if (!(latestMergedReceipt is null)) { mergeableReceipts.Insert(0, latestMergedReceipt); } UnitsRange unitsRange = new UnitsRange(rangeFrom, rangeTo); var receiptsToMerge = mergeableReceipts.Select(r => new DataDeliveryReceiptToMerge( r.Request.UnitsRange, r.Receipt.Signature)).OrderBy(r => r.UnitsRange.To).ToList(); uint number = deposit.GetNextReceiptRequestNumber(); if (_logger.IsInfo) { _logger.Info($"Created merged receipt request for deposit: '{deposit.DepositId}' [{rangeFrom}, {rangeTo}]."); } return(new DataDeliveryReceiptRequest(number, deposit.DepositId, unitsRange, receiptsToMerge: receiptsToMerge)); }
private async Task ClaimPaymentAsync(IDepositNodesHandler deposit) { if (deposit.LatestPaymentClaim?.UnitsRange.To == deposit.PurchasedUnits - 1) { if (_logger.IsInfo) { _logger.Info($"Last receipt request for deposit: '{deposit.DepositId}' was already claimed."); } return; } var claimableReceipts = deposit.Receipts .Where(r => !r.IsClaimed) .OrderBy(r => r.Timestamp) .ThenBy(r => r.Request.UnitsRange.To) .ToArray(); DataDeliveryReceiptDetails latestClaimableReceipt = claimableReceipts.LastOrDefault(r => r.IsMerged) ?? claimableReceipts.FirstOrDefault(r => r.Request.UnitsRange.To == deposit.PurchasedUnits - 1); if (latestClaimableReceipt is null) { if (_logger.IsWarn) { _logger.Warn($"Cannot claim a payment for deposit: '{deposit.DepositId}' - claimable receipt was not found."); } return; } if (latestClaimableReceipt.IsClaimed) { if (_logger.IsWarn) { _logger.Warn($"Cannot claim a payment for deposit: '{deposit.DepositId}' - receipt was already claimed, timestamp: {latestClaimableReceipt.Timestamp}."); } return; } DataDeliveryReceiptDetails?receipt = await _receiptRepository.GetAsync(latestClaimableReceipt.Id); if (receipt == null) { throw new InvalidDataException($"Unable to make a claim for the receipt with ID: {latestClaimableReceipt.Id} - receipt missing"); } receipt.Claim(); await _receiptRepository.UpdateAsync(receipt); PaymentClaim?paymentClaim = await _paymentClaimProcessor.ProcessAsync(latestClaimableReceipt.Request, latestClaimableReceipt.Receipt.Signature); if (paymentClaim is null) { throw new InvalidDataException($"Unable to make a claim for the receipt with ID: {latestClaimableReceipt.Id} - claim processing failure"); } latestClaimableReceipt.Claim(); deposit.AddReceipt(latestClaimableReceipt); // so the receipt become LatestReceipt deposit.ClearReceipts(); deposit.SetLatestPaymentClaim(paymentClaim); uint claimedUnits = deposit.UnclaimedUnits < paymentClaim.ClaimedUnits ? deposit.UnclaimedUnits : paymentClaim.ClaimedUnits; deposit.SubtractUnclaimedUnits(claimedUnits); if (_logger.IsInfo) { _logger.Info($"Successfully claimed payment ({claimedUnits} units) for deposit: '{deposit.DepositId}'."); } }
private async Task TryMergeReceiptsAsync(IDepositNodesHandler deposit) { Keccak depositId = deposit.DepositId; if (deposit.HasSentLastMergedReceipt) { if (_logger.IsInfo) { _logger.Info($"Last receipt request for deposit: '{depositId}' was already sent."); } return; } if (deposit.HasClaimedAllUnits) { if (_logger.IsInfo) { _logger.Info($"Last receipt request for deposit: '{depositId}' was already claimed."); } return; } DataDeliveryReceiptDetails?latestReceipt = deposit.LatestMergedReceipt; if (latestReceipt?.Request.UnitsRange.To == deposit.PurchasedUnits) { if (_logger.IsInfo) { _logger.Info($"Last receipt request for deposit: '{depositId}' was already merged."); } return; } var consumerNodes = _sessionManager.GetConsumerNodes(depositId); foreach (ConsumerNode node in consumerNodes) { if (_logger.IsInfo) { _logger.Info($"Trying to merge receipts for deposit: '{depositId}' with node: '{node.Peer.NodeId}'."); } try { ProviderSession?session = node.GetSession(depositId); if (session is null) { if (_logger.IsInfo) { _logger.Info($"Session was not found for deposit: '{depositId}', node: '{node.Peer.NodeId}'."); } continue; } DataDeliveryReceiptRequest?request = CreateMergedRequest(deposit); if (request is null) { if (_logger.IsInfo) { _logger.Info($"Merged receipt for deposit: '{depositId}' couldn't be created."); } return; } if (latestReceipt?.Request.UnitsRange.Equals(request.UnitsRange) == true) { if (_logger.IsInfo) { _logger.Info($"Merged receipt request for deposit: '{depositId}' would be the same as a previous one (already sent)."); } continue; } uint previouslyMergedUnits = latestReceipt?.Request.UnitsRange.To + 1 ?? 0; DataDeliveryReceiptDetails?receiptDetails = await TryHandleReceiptAsync(session, deposit.Consumer, node.Peer, request); if (receiptDetails is null) { if (_logger.IsWarn) { _logger.Warn($"Couldn't merge receipts for deposit: '{depositId}' with node: '{node.Peer.NodeId}'."); } continue; } deposit.AddReceipt(receiptDetails); UnitsRange range = receiptDetails.Request.UnitsRange; uint mergedTo = range.To + 1; if (mergedTo <= previouslyMergedUnits) { return; } uint mergedUnits = mergedTo - previouslyMergedUnits; mergedUnits = deposit.UnmergedUnits < mergedUnits ? deposit.UnmergedUnits : mergedUnits; deposit.SubtractUnmergedUnits(mergedUnits); if (_logger.IsInfo) { _logger.Info($"Successfully merged receipts ({mergedUnits} units) for deposit: '{depositId}' with node: '{node.Peer.NodeId}'."); } break; } catch (Exception ex) { if (_logger.IsWarn) { _logger.Warn($"There was an error when merging receipt requests for deposit: '{deposit.DepositId}'. {ex}"); } } } }
private async Task RequestReceiptsAsync(IDepositNodesHandler deposit) { Keccak depositId = deposit.DepositId; int nodesCount = _sessionManager.GetNodesCount(depositId); int nodesCounter = 0; foreach (ConsumerNode node in _sessionManager.GetConsumerNodes(depositId)) { nodesCounter++; if (deposit.HasSentAllReceipts) { if (_logger.IsInfo) { _logger.Info($"Last receipt request for deposit: '{depositId}' was already sent."); } return; } if (deposit.HasClaimedAllUnits) { if (_logger.IsInfo) { _logger.Info($"Last receipt request for deposit: '{depositId}' was already claimed."); } return; } ProviderSession?session = node.GetSession(depositId); if (session is null) { if (_logger.IsInfo) { _logger.Info($"Session was not found for deposit: '{depositId}', node: '{node.Peer.NodeId}'."); } continue; } if (session.UnpaidUnits == 0) { if (_logger.IsInfo) { _logger.Info($"Session: '{session.Id}' has no unpaid units."); } continue; } if (_logger.IsTrace) { _logger.Trace($"Session: '{session.Id}' has {session.UnpaidUnits} unpaid units."); } if (!deposit.ConsumedAll && await _receiptsPolicies.CanRequestReceipts(session.UnpaidUnits * nodesCount, deposit.UnitPrice) == false) { if (_logger.IsTrace) { _logger.Trace($"Session: '{session.Id}' has too low unpaid units to be processed."); } continue; } if (_logger.IsInfo) { _logger.Info($"Requesting receipt for deposit: '{depositId}' from node: '{node.Peer.NodeId}' ({nodesCounter}/{nodesCount})."); } DataDeliveryReceiptDetails?details = await TryHandleReceiptAsync(session, deposit.Consumer, node.Peer); if (details is null) { if (_logger.IsInfo) { _logger.Info($"Couldn't request receipt for deposit: '{depositId}' from node: '{node.Peer.NodeId}'."); } continue; } if (_logger.IsInfo) { _logger.Info($"Successfully requested receipt for deposit: '{depositId}' from node: '{node.Peer.NodeId}'."); } } }
private async Task TryHandleDepositAsync(IDepositNodesHandler deposit, bool isRetry = false) { if (_logger.IsTrace) { _logger.Trace($"Started handling consumed units for deposit: '{deposit.DepositId}'."); } if (deposit.ConsumedAll || await _receiptsPolicies.CanRequestReceipts(deposit.UnpaidUnits, deposit.UnitPrice)) { await RequestReceiptsAsync(deposit); } if (deposit.ConsumedAll || await _receiptsPolicies.CanMergeReceipts(deposit.UnmergedUnits, deposit.UnitPrice)) { await TryMergeReceiptsAsync(deposit); } await TryClaimPaymentAsync(deposit); if (_logger.IsTrace) { _logger.Trace($"Finished handling consumed units for deposit: '{deposit.DepositId}'."); } if (isRetry) { return; } if (!deposit.ConsumedAll) { return; } if (deposit.HasClaimedAllUnits) { if (_logger.IsInfo) { _logger.Info($"Last payment claim for deposit: '{deposit.DepositId}' was already processed."); } return; } deposit.FinishHandling(); while (deposit.CurrentLastClaimRetry < LastClaimRetries) { if (_logger.IsInfo) { _logger.Info($"Missing payment claim for deposit: '{deposit.DepositId}', retry {deposit.CurrentLastClaimRetry + 1}/{LastClaimRetries}."); } foreach (ConsumerNode node in _sessionManager.GetConsumerNodes(deposit.DepositId)) { ProviderSession?session = node.GetSession(deposit.DepositId); if (session is null) { if (_logger.IsInfo) { _logger.Info($"Session was not found for deposit: '{deposit.DepositId}', node: '{node.Peer.NodeId}'."); } continue; } if (_logger.IsInfo) { _logger.Info($"Consumer: '{node.Peer.NodeId}', session: '{session.Id}' has {session.UnpaidUnits} unpaid units."); } } await TryHandleDepositAsync(deposit, true); deposit.IncrementLastClaimRetries(); if (deposit.HasClaimedAllUnits) { return; } } }