예제 #1
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}].");
            }
        }
예제 #2
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);
            }
        }