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 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}"); } } } }