Example #1
0
 public DataDeliveryReceiptRequestForRpc(DataDeliveryReceiptRequest request)
 {
     Number       = request.Number;
     DepositId    = request.DepositId;
     UnitsRange   = new UnitsRangeForRpc(request.UnitsRange);
     IsSettlement = request.IsSettlement;
 }
Example #2
0
        public async Task <PaymentClaim?> ProcessAsync(DataDeliveryReceiptRequest receiptRequest, Signature signature)
        {
            var depositId = receiptRequest.DepositId;

            if (_logger.IsWarn)
            {
                _logger.Warn($"NDM provider instantly verifying payment claim for deposit: '{depositId}'...");
            }
            var paymentClaim = await _processor.ProcessAsync(receiptRequest, signature);

            if (paymentClaim is null)
            {
                return(null);
            }

            paymentClaim.SetTransactionCost(0);
            await _repository.UpdateAsync(paymentClaim);

            if (_logger.IsWarn)
            {
                _logger.Warn($"NDM provider instantly verified payment claim (id: '{paymentClaim.Id}') for deposit: '{depositId}'.");
            }

            return(paymentClaim);
        }
        public async Task <DataDeliveryReceipt> SendRequestDataDeliveryReceiptAsync(
            DataDeliveryReceiptRequest receiptRequest, CancellationToken?token = null)
        {
            if (Logger.IsTrace)
            {
                Logger.Trace($"{Session.RemoteNodeId} NDM sending: requestdatadeliveryreceipt");
            }
            var cancellationToken = token ?? CancellationToken.None;
            var message           = new RequestDataDeliveryReceiptMessage(receiptRequest);
            var request           = new Request <RequestDataDeliveryReceiptMessage, DataDeliveryReceipt>(message);

            _receiptsRequests.Add(request, cancellationToken);
            Send(request.Message);
            var task      = request.CompletionSource.Task;
            var firstTask = await Task.WhenAny(task, Task.Delay(Timeouts.NdmDeliveryReceipt, cancellationToken));

            if (firstTask.IsCanceled)
            {
                cancellationToken.ThrowIfCancellationRequested();
            }

            if (firstTask != task)
            {
                throw new TimeoutException($"{Session.RemoteNodeId} Request timeout in " +
                                           $"{nameof(RequestDataDeliveryReceiptMessage)}");
            }

            return(task.Result);
        }
Example #4
0
        public async Task given_null_transaction_hash_returned_payment_claim_should_have_status_unknown()
        {
            var unitsRange = new UnitsRange(0, 1);
            var consumer   = CreateConsumer();

            _consumerRepository.GetAsync(Arg.Any <Keccak>()).Returns(consumer);
            var receiptRequest = new DataDeliveryReceiptRequest(1, Keccak.Zero, unitsRange);
            var paymentClaim   = await _processor.ProcessAsync(receiptRequest, null);

            paymentClaim.Transaction.Should().BeNull();
            paymentClaim.Status.Should().Be(PaymentClaimStatus.Unknown);
        }
Example #5
0
        public async Task given_valid_transaction_hash_returned_payment_claim_should_have_status_sent()
        {
            var unitsRange = new UnitsRange(0, 1);
            var consumer   = CreateConsumer();

            _consumerRepository.GetAsync(Arg.Any <Keccak>()).Returns(consumer);
            _paymentService.ClaimPaymentAsync(Arg.Any <PaymentClaim>(), _coldWalletAddress, _gasPrice)
            .Returns(_transaction.Hash);
            var receiptRequest = new DataDeliveryReceiptRequest(1, Keccak.Zero, unitsRange);
            var paymentClaim   = await _processor.ProcessAsync(receiptRequest, null);

            paymentClaim.Transaction.Hash.Should().Be(_transaction.Hash);
            paymentClaim.Status.Should().Be(PaymentClaimStatus.Sent);
        }
Example #6
0
        public async Task given_receipt_request_range_0_to_19_payment_claim_range_should_be_0_to_19()
        {
            var unitsRange = new UnitsRange(0, 19);
            var consumer   = CreateConsumer();

            _consumerRepository.GetAsync(Arg.Any <Keccak>()).Returns(consumer);
            _paymentService.ClaimPaymentAsync(Arg.Any <PaymentClaim>(), _coldWalletAddress, _gasPrice)
            .Returns(_transaction.Hash);
            var receiptRequest = new DataDeliveryReceiptRequest(1, Keccak.Zero, unitsRange);
            var paymentClaim   = await _processor.ProcessAsync(receiptRequest, null);

            paymentClaim.ClaimedUnits.Should().Be(20);
            paymentClaim.UnitsRange.Should().Be(unitsRange);
        }
 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;
 }
Example #8
0
        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));
        }
Example #10
0
        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);
        }
Example #11
0
        private async Task <DataDeliveryReceiptDetails?> TryHandleReceiptAsync(ProviderSession session, Address consumer,
                                                                               INdmProviderPeer peer, ProviderSession?previousSession = null)
        {
            Keccak depositId = session.DepositId;

            try
            {
                bool isSettlement                  = !(previousSession is null);
                uint unpaidSessionUnits            = previousSession?.UnpaidUnits ?? session.UnpaidUnits;
                DataDeliveryReceiptRequest request = CreateRequest(depositId, session.Id, unpaidSessionUnits, isSettlement);

                return(await TryHandleReceiptAsync(session, consumer, peer, request));
            }
            catch (Exception ex)
            {
                if (_logger.IsError)
                {
                    _logger.Error($"There was an error when handling the receipts for deposit: '{depositId}', node: '{peer.NodeId}'.{ex}", ex);
                }
            }

            return(null);
        }
Example #12
0
        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}].");
            }
        }
Example #13
0
        private static DataDeliveryReceiptRequest GetDataDeliveryReceiptRequest()
        {
            var receipt = new DataDeliveryReceiptRequest(1, TestItem.KeccakA, new UnitsRange(0, 5));

            return(receipt);
        }
Example #14
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);
            }
        }
        public async Task <PaymentClaim?> ProcessAsync(DataDeliveryReceiptRequest receiptRequest, Signature signature)
        {
            Keccak   depositId = receiptRequest.DepositId;
            Consumer?consumer  = await _consumerRepository.GetAsync(depositId);

            if (consumer is null)
            {
                if (_logger.IsError)
                {
                    _logger.Error($"Could not find any consumers for deposit {depositId} in the repository.");
                }
                return(null);
            }

            DataRequest  dataRequest   = consumer.DataRequest;
            UnitsRange   range         = receiptRequest.UnitsRange;
            uint         claimedUnits  = range.Units;
            BigInteger   claimedValue  = claimedUnits * (BigInteger)consumer.DataRequest.Value / consumer.DataRequest.Units;
            ulong        timestamp     = _timestamper.UnixTime.Seconds;
            Rlp          unitsRangeRlp = _unitsRangeRlpDecoder.Encode(range);
            Keccak       id            = Keccak.Compute(Rlp.Encode(Rlp.Encode(depositId), Rlp.Encode(timestamp), unitsRangeRlp).Bytes);
            PaymentClaim paymentClaim  = new PaymentClaim(id, consumer.DepositId, consumer.DataAsset.Id,
                                                          consumer.DataAsset.Name, dataRequest.Units, claimedUnits, range, dataRequest.Value,
                                                          (UInt256)claimedValue, dataRequest.ExpiryTime, dataRequest.Pepper, dataRequest.Provider,
                                                          dataRequest.Consumer, signature, timestamp, Array.Empty <TransactionInfo>(), PaymentClaimStatus.Unknown);
            await _paymentClaimRepository.AddAsync(paymentClaim);

            if (_logger.IsInfo)
            {
                _logger.Info($"Claiming a payment (id: '{paymentClaim.Id}') for deposit: '{depositId}', range: [{range.From}, {range.To}], units: {claimedUnits}.");
            }
            UInt256 gasPrice = await _gasPriceService.GetCurrentPaymentClaimGasPriceAsync();

            Keccak?transactionHash = null;

            if (_disableSendingPaymentClaimTransaction)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn("*** NDM provider sending payment claim transaction is disabled ***");
                }
            }
            else
            {
                transactionHash = await SendTransactionAsync(paymentClaim, gasPrice);

                if (transactionHash is null)
                {
                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Payment claim (id: {paymentClaim.Id}) for deposit: '{paymentClaim.DepositId}' did not receive a transaction hash.");
                    }
                    return(paymentClaim);
                }
            }

            if (_logger.IsInfo)
            {
                _logger.Info($"Payment claim (id: {paymentClaim.Id}) for deposit: '{paymentClaim.DepositId}' received a transaction hash: '{transactionHash}'.");
            }
            paymentClaim.AddTransaction(TransactionInfo.Default(transactionHash, 0, gasPrice, _paymentService.GasLimit,
                                                                timestamp));
            paymentClaim.SetStatus(PaymentClaimStatus.Sent);
            await _paymentClaimRepository.UpdateAsync(paymentClaim);

            string claimedEth = ((decimal)claimedValue / Eth).ToString("0.0000");

            if (_logger.IsInfo)
            {
                _logger.Info($"Sent a payment claim (id: '{paymentClaim.Id}') for deposit: '{depositId}', range: [{range.From}, {range.To}], units: {claimedUnits}, units: {claimedUnits}, value: {claimedValue} wei ({claimedEth} ETH, transaction hash: '{transactionHash}'.");
            }

            return(paymentClaim);
        }
        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);
        }
Example #17
0
 public Task SendDataDeliveryReceiptAsync(DataDeliveryReceiptRequest request)
 => _receiptService.SendAsync(request);
        public bool IsValid(DataDeliveryReceiptRequest receiptRequest, long unpaidUnits, long consumedUnits,
                            long purchasedUnits)
        {
            var requestedUnitsRange = receiptRequest.UnitsRange;
            var from           = requestedUnitsRange.From;
            var to             = requestedUnitsRange.To;
            var requestedUnits = to - from + 1;
            var isMerged       = receiptRequest.ReceiptsToMerge.Any();

            if (_logger.IsInfo)
            {
                _logger.Info($"Requested units range{(isMerged ? " (merged)" : string.Empty)}: [{from}, {to}] for deposit: '{receiptRequest.DepositId}'. Unpaid units: {unpaidUnits}, requested: {requestedUnits}.");
            }
            if (requestedUnits > purchasedUnits || to >= purchasedUnits)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Invalid receipt request, requested units: {requestedUnits}, purchased: {purchasedUnits} <= {to} (range to).");
                }

                return(false);
            }

            if (requestedUnits <= unpaidUnits)
            {
                return(true);
            }

            if (!isMerged)
            {
                var consumedAndUnpaidUnits = consumedUnits + unpaidUnits;
                if (_logger.IsInfo)
                {
                    _logger.Info($"No merged receipts provided for deposit: '{receiptRequest.DepositId}', validating: {requestedUnits} <= {unpaidUnits} OR {from} == 0 AND {requestedUnits} <= {consumedAndUnpaidUnits}");
                }

                return(requestedUnits <= unpaidUnits || from == 0 && requestedUnits <= consumedAndUnpaidUnits);
            }

            var subsets      = new List <UnitsRange>();
            var subsetsRange = 0L;

            var receiptsToMerge = receiptRequest.ReceiptsToMerge.ToArray();

            if (receiptsToMerge.Length == 1)
            {
                var previousRange = receiptsToMerge[0].UnitsRange;
                if (_logger.IsInfo)
                {
                    _logger.Info($"Previous units range: [{previousRange.From}, {previousRange.To}] for deposit: '{receiptRequest.DepositId}'.");
                }

                if (receiptRequest.UnitsRange.Equals(previousRange))
                {
                    return(true);
                }

                return(previousRange.To - previousRange.From + 1 + unpaidUnits >= requestedUnits &&
                       ((requestedUnitsRange.To > previousRange.To
                               ? requestedUnitsRange.To - previousRange.To
                               : previousRange.To - requestedUnitsRange.To) <= unpaidUnits));
            }

            for (var i = 0; i < receiptsToMerge.Length; i++)
            {
                var mergedUnitsRange = receiptsToMerge[i].UnitsRange;

                for (var j = 0; j < receiptsToMerge.Length; j++)
                {
                    if (i == j)
                    {
                        continue;
                    }

                    var mergedUnitsRangeToCompare = receiptsToMerge[j].UnitsRange;
                    if (mergedUnitsRange.Equals(mergedUnitsRangeToCompare))
                    {
                        return(false);
                    }

                    if (mergedUnitsRange.IsSubsetOf(mergedUnitsRangeToCompare))
                    {
                        continue;
                    }

                    if (mergedUnitsRange.IntersectsWith(mergedUnitsRangeToCompare))
                    {
                        return(false);
                    }
                }

                if (mergedUnitsRange.Equals(requestedUnitsRange))
                {
                    return(false);
                }

                if (mergedUnitsRange.IsSubsetOf(requestedUnitsRange))
                {
                    subsets.Add(mergedUnitsRange);
                    subsetsRange += mergedUnitsRange.To - mergedUnitsRange.From + 1;
                    continue;
                }

                if (mergedUnitsRange.IntersectsWith(requestedUnitsRange))
                {
                    return(false);
                }
            }

            if (!subsets.Any())
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"No merged receipts subsets found, validating: {requestedUnits} <= {unpaidUnits}.");
                }

                return(requestedUnits <= unpaidUnits);
            }

            if (_logger.IsInfo)
            {
                _logger.Info($"Merged receipts subsets found, validating: {requestedUnits} - {subsetsRange} <= {unpaidUnits}.");
            }

            return(requestedUnits - subsetsRange <= unpaidUnits);
        }
Example #19
0
 public RequestDataDeliveryReceiptMessage(DataDeliveryReceiptRequest request)
 {
     Request = request;
 }
Example #20
0
        private async Task <DataDeliveryReceiptDetails?> TryHandleReceiptAsync(ProviderSession session, Address consumer,
                                                                               INdmProviderPeer peer, DataDeliveryReceiptRequest request)
        {
            Keccak depositId             = session.DepositId;
            IDepositNodesHandler?deposit = GetDepositNodesHandler(depositId);

            if (deposit == null)
            {
                throw new InvalidDataException($"Cannot resolve deposit handle for deposit ID: {depositId}");
            }

            try
            {
                UnitsRange unitsRange = request.UnitsRange;
                if (_logger.IsInfo)
                {
                    _logger.Info($"Sending data delivery receipt request ({request.Number}), deposit: '{depositId}', session: '{session.Id}', range: [{unitsRange.From}, {unitsRange.To}].");
                }
                if (request.ReceiptsToMerge.Any())
                {
                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Receipts to merge for deposit: '{depositId}': {string.Join(", ", request.ReceiptsToMerge.Select(r => $"[{r.UnitsRange.From}, {r.UnitsRange.To}]"))}");
                    }
                }

                (DataDeliveryReceipt receipt, RequestReceiptStatus status) = await TryHandleDeliveryReceipt(deposit, consumer, session, request, peer);

                Keccak id = Keccak.Compute(Rlp.Encode(Rlp.Encode(depositId), Rlp.Encode(request.Number)).Bytes);
                DataDeliveryReceiptDetails receiptDetails = new DataDeliveryReceiptDetails(id, session.Id, session.DataAssetId, peer.NodeId,
                                                                                           request, receipt, _timestamper.UnixTime.Seconds, false);
                await _receiptRepository.AddAsync(receiptDetails);

                if (status != RequestReceiptStatus.Ok)
                {
                    return(null);
                }

                UnitsRange range        = request.UnitsRange;
                uint       claimedUnits = range.To - range.From + 1;
                deposit.AddReceipt(receiptDetails);
                if (!request.ReceiptsToMerge.Any())
                {
                    deposit.SubtractUnpaidUnits(claimedUnits);
                }

                if (_logger.IsInfo)
                {
                    _logger.Info($"Successfully processed receipt ({claimedUnits} units) for deposit: '{depositId}' with node: '{peer.NodeId}'.");
                }

                return(receiptDetails);
            }
            catch (Exception ex)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"There was an error when processing a receipt for deposit: '{depositId}'. {ex}");
                }
                session.SetDataAvailability(DataAvailability.DataDeliveryReceiptNotProvided);
                await _sessionRepository.UpdateAsync(session);

                peer.SendDataAvailability(depositId, DataAvailability.DataDeliveryReceiptInvalid);

                return(null);
            }
        }