public DataDeliveryReceiptReportItem(Keccak id, uint number, Keccak sessionId, PublicKey nodeId, DataDeliveryReceiptRequest request, DataDeliveryReceipt receipt, ulong timestamp, bool isMerged, bool isClaimed) { Id = id; Number = number; SessionId = sessionId; NodeId = nodeId; Request = request; Receipt = receipt; Timestamp = timestamp; IsMerged = isMerged; IsClaimed = isClaimed; }
public async Task given_invalid_consumer_address_delivery_receipt_should_not_be_processed() { _signer.RecoverPublicKey(Arg.Any <Signature>(), Arg.Any <Keccak>()).ReturnsForAnyArgs(TestItem.PublicKeyB); _peer.ConsumerAddress.Returns(TestItem.AddressC); var currentReceiptRequestUnitsRange = new UnitsRange(0, 9); var receiptRequest = new DataDeliveryReceiptRequest(1, Keccak.Zero, currentReceiptRequestUnitsRange); var deliveryReceipt = new DataDeliveryReceipt(StatusCodes.Ok, 0, 0, new Signature(new byte[65])); var wasProcessed = await _processor.TryProcessAsync(_session, _publicKey.Address, _peer, receiptRequest, deliveryReceipt); wasProcessed.Should().BeFalse(); _session.DataAvailability.Should().Be(DataAvailability.DataDeliveryReceiptInvalid); }
public DataDeliveryReceiptDetails Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { rlpStream.ReadSequenceLength(); Keccak id = rlpStream.DecodeKeccak(); Keccak sessionId = rlpStream.DecodeKeccak(); Keccak dataAssetId = rlpStream.DecodeKeccak(); PublicKey consumerNodeId = new PublicKey(rlpStream.DecodeByteArray()); DataDeliveryReceiptRequest request = Serialization.Rlp.Rlp.Decode <DataDeliveryReceiptRequest>(rlpStream); DataDeliveryReceipt receipt = Serialization.Rlp.Rlp.Decode <DataDeliveryReceipt>(rlpStream); ulong timestamp = rlpStream.DecodeUlong(); bool isClaimed = rlpStream.DecodeBool(); return(new DataDeliveryReceiptDetails(id, sessionId, dataAssetId, consumerNodeId, request, receipt, timestamp, isClaimed)); }
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); }
public DataDeliveryReceiptForRpc(DataDeliveryReceipt receipt) { StatusCode = receipt.StatusCode.ToString().ToLowerInvariant(); ConsumedUnits = receipt.ConsumedUnits; UnpaidUnits = receipt.UnpaidUnits; }
public DataDeliveryReceiptMessage(Keccak depositId, DataDeliveryReceipt receipt) { DepositId = depositId; Receipt = receipt; }
public async Task SendAsync(DataDeliveryReceiptRequest request, int fetchSessionRetries = 3, int fetchSessionRetryDelayMilliseconds = 3000) { var depositId = request.DepositId; var(deposit, session) = await TryGetDepositAndSessionAsync(depositId, fetchSessionRetries, fetchSessionRetryDelayMilliseconds); if (deposit is null || session is null) { return; } var providerAddress = deposit.DataAsset.Provider.Address; var providerPeer = _providerService.GetPeer(providerAddress); if (providerPeer is null) { if (_logger.IsWarn) { _logger.Warn($"Provider: '{providerAddress}' was not found."); } return; } var receiptId = Keccak.Compute(Rlp.Encode(Rlp.Encode(depositId), Rlp.Encode(request.Number), Rlp.Encode(_timestamper.EpochSeconds))); if (!_receiptRequestValidator.IsValid(request, session.UnpaidUnits, session.ConsumedUnits, deposit.Deposit.Units)) { if (_logger.IsWarn) { _logger.Warn($"Provider: '{providerPeer.NodeId}' sent an invalid data delivery receipt request."); } var receipt = new DataDeliveryReceipt(StatusCodes.InvalidReceiptRequestRange, session.ConsumedUnits, session.UnpaidUnits, new Signature(1, 1, 27)); await _receiptRepository.AddAsync(new DataDeliveryReceiptDetails(receiptId, session.Id, session.DataAssetId, _nodePublicKey, request, receipt, _timestamper.EpochSeconds, false)); await _sessionRepository.UpdateAsync(session); providerPeer.SendDataDeliveryReceipt(depositId, receipt); return; } var abiHash = _abiEncoder.Encode(AbiEncodingStyle.Packed, _dataDeliveryReceiptAbiSig, depositId.Bytes, new[] { request.UnitsRange.From, request.UnitsRange.To }); var receiptHash = Keccak.Compute(abiHash); var signature = _wallet.Sign(receiptHash, deposit.Consumer); var recoveredAddress = _ecdsa.RecoverPublicKey(signature, receiptHash)?.Address; if (deposit.Consumer != recoveredAddress) { if (_logger.IsError) { _logger.Error($"Signature failure when signing the receipt from provider: '{providerPeer.NodeId}', invalid recovered address."); } var receipt = new DataDeliveryReceipt(StatusCodes.InvalidReceiptAddress, session.ConsumedUnits, session.UnpaidUnits, new Signature(1, 1, 27)); await _receiptRepository.AddAsync(new DataDeliveryReceiptDetails(receiptId, session.Id, session.DataAssetId, _nodePublicKey, request, receipt, _timestamper.EpochSeconds, false)); await _sessionRepository.UpdateAsync(session); providerPeer.SendDataDeliveryReceipt(depositId, receipt); return; } if (_logger.IsInfo) { _logger.Info($"Provider: '{providerPeer.NodeId}' sent a valid data delivery receipt request."); } if (request.ReceiptsToMerge.Any()) { if (_logger.IsInfo) { _logger.Info($"Processing a merged receipt request for deposit: {session.DepositId}, session: '{session.Id} - units will not be updated."); } } else { var paidUnits = request.UnitsRange.To - request.UnitsRange.From + 1; var unpaidUnits = session.UnpaidUnits > paidUnits ? session.UnpaidUnits - paidUnits : 0; session.SetUnpaidUnits(unpaidUnits); session.AddPaidUnits(paidUnits); if (request.IsSettlement) { session.SetPaidUnits(paidUnits); session.SettleUnits(paidUnits); if (_logger.IsInfo) { _logger.Info($"Settled {paidUnits} units for deposit: '{session.DepositId}', session: '{session.Id}'."); } } await _sessionRepository.UpdateAsync(session); } if (_logger.IsInfo) { _logger.Info($"Sending data delivery receipt for deposit: '{depositId}', range: [{request.UnitsRange.From}, {request.UnitsRange.To}]."); } var deliveryReceipt = new DataDeliveryReceipt(StatusCodes.Ok, session.ConsumedUnits, session.UnpaidUnits, signature); await _receiptRepository.AddAsync(new DataDeliveryReceiptDetails(receiptId, session.Id, session.DataAssetId, _nodePublicKey, request, deliveryReceipt, _timestamper.EpochSeconds, false)); providerPeer.SendDataDeliveryReceipt(depositId, deliveryReceipt); if (_logger.IsInfo) { _logger.Info($"Sent data delivery receipt for deposit: '{depositId}', range: [{request.UnitsRange.From}, {request.UnitsRange.To}]."); } }
public async Task <bool> TryProcessAsync(ProviderSession session, Address consumer, INdmProviderPeer peer, DataDeliveryReceiptRequest receiptRequest, DataDeliveryReceipt deliveryReceipt) { var depositId = session.DepositId; var unitsRange = receiptRequest.UnitsRange; var abiHash = _abiEncoder.Encode(AbiEncodingStyle.Packed, _dataDeliveryReceiptAbiSig, receiptRequest.DepositId.Bytes, new[] { unitsRange.From, unitsRange.To }); var address = _ecdsa.RecoverPublicKey(deliveryReceipt.Signature, Keccak.Compute(abiHash)).Address; if (!consumer.Equals(address)) { if (_logger.IsWarn) { _logger.Warn($"Recovered an invalid address: '{address}' (should be: '{consumer}') for delivery receipt for deposit: '{depositId}', consumer: '{session.ConsumerAddress}', session: '{session.Id}'."); } session.SetDataAvailability(DataAvailability.DataDeliveryReceiptInvalid); await _sessionRepository.UpdateAsync(session); peer.SendDataAvailability(depositId, DataAvailability.DataDeliveryReceiptInvalid); return(false); } var paidUnits = unitsRange.To - unitsRange.From + 1; if (_logger.IsInfo) { _logger.Info($"Consumer: '{consumer}' has provided a valid receipt for deposit: '{receiptRequest.DepositId}', range: [{unitsRange.From}, {unitsRange.To}], paid units: {paidUnits}"); } if (receiptRequest.ReceiptsToMerge.Any()) { if (_logger.IsInfo) { _logger.Info($"Processing a merged receipt request for consumer: {session.ConsumerAddress}, session: '{session.Id} - units will not be updated."); } } else { if (_logger.IsInfo) { _logger.Info($"Processing a receipt request for deposit: '{receiptRequest.DepositId}', consumer: {session.ConsumerAddress}, session: '{session.Id} - units will be updated."); } var unpaidUnits = session.UnpaidUnits > paidUnits ? session.UnpaidUnits - paidUnits : 0; session.SetUnpaidUnits(unpaidUnits); session.AddPaidUnits(paidUnits); if (receiptRequest.IsSettlement) { session.SetPaidUnits(paidUnits); session.SettleUnits(paidUnits); if (_logger.IsInfo) { _logger.Info($"Settled {paidUnits} units for deposit: '{receiptRequest.DepositId}', consumer: {session.ConsumerAddress}, session: '{session.Id}'."); } } await _sessionRepository.UpdateAsync(session); } var dataAvailability = session.DataAvailability; if (dataAvailability == DataAvailability.DataDeliveryReceiptInvalid || dataAvailability == DataAvailability.DataDeliveryReceiptNotProvided) { session.SetDataAvailability(DataAvailability.Available); await _sessionRepository.UpdateAsync(session); if (_logger.IsInfo) { _logger.Info($"Updated previously set data availability: '{dataAvailability}' -> '{DataAvailability.Available}', consumer: {session.ConsumerAddress}, session: '{session.Id}'."); } } return(true); }
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); } }