public async Task send_should_succeed_when_receipt_is_valid() { var timestamp = _timestamper.EpochSeconds; var receipt = GetDataDeliveryReceiptRequest(); var deposit = GetDepositDetails(); var session = GetConsumerSession(); var provider = Substitute.For <INdmPeer>(); var receiptId = Keccak.Compute(Rlp.Encode(Rlp.Encode(receipt.DepositId), Rlp.Encode(receipt.Number), Rlp.Encode(timestamp)).Bytes); _depositProvider.GetAsync(receipt.DepositId).Returns(deposit); _sessionService.GetActive(receipt.DepositId).Returns(session); _providerService.GetPeer(deposit.DataAsset.Provider.Address).Returns(provider); _receiptRequestValidator.IsValid(receipt, session.UnpaidUnits, session.ConsumedUnits, deposit.Deposit.Units).Returns(true); _ecdsa.RecoverPublicKey(Arg.Any <Signature>(), Arg.Any <Keccak>()) .Returns(session.ConsumerNodeId); await _receiptService.SendAsync(receipt, 0, 0); await _sessionRepository.Received().UpdateAsync(session); await _receiptRepository.Received().AddAsync(Arg.Is <DataDeliveryReceiptDetails>(x => x.Id == receiptId && x.SessionId == session.Id && x.DataAssetId == session.DataAssetId && x.ConsumerNodeId == _nodePublicKey && x.Request.Equals(receipt) && x.Timestamp == timestamp && !x.IsClaimed)); provider.Received().SendDataDeliveryReceipt(receipt.DepositId, Arg.Is <DataDeliveryReceipt>(x => x.StatusCode == StatusCodes.Ok)); }
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}]."); } }