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