예제 #1
0
        private async Task <(ConsumerSession session, DepositDetails deposit)> TryGetSessionAndDepositAsync(
            Keccak depositId)
        {
            var session = _sessionService.GetActive(depositId);

            if (session is null)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Session for deposit: '{depositId}' was not found.");
                }

                return(null, null);
            }

            var deposit = await _depositProvider.GetAsync(depositId);

            if (deposit is null)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Deposit: '{depositId}' was not found.");
                }

                return(session, null);
            }

            return(session, deposit);
        }
예제 #2
0
        public void Setup()
        {
            IConsumerNotifier   notifier           = new ConsumerNotifier(Substitute.For <INdmNotifier>());
            DepositsInMemoryDb  db                 = new DepositsInMemoryDb();
            IProviderRepository providerRepository = new ProviderInMemoryRepository(db);
            DataAssetProvider   provider           = new DataAssetProvider(_providerAddress, "provider");
            DataAssetService    dataAssetService   = new DataAssetService(providerRepository, notifier, LimboLogs.Instance);

            _asset1 = new DataAsset(_asset1Id, "name", "desc", 1, DataAssetUnitType.Unit, 1000, 10000, new DataAssetRules(new DataAssetRule(1), new DataAssetRule(100)), provider, state: DataAssetState.Published);
            dataAssetService.AddDiscovered(_asset1, _ndmPeer);
            _deposit1 = new Deposit(_deposit1Id, 1, 2, 3);
            _details1 = new DepositDetails(_deposit1, _asset1, Address.Zero, new byte[0], 1, new TransactionInfo[0], 1);

            _asset2 = new DataAsset(_asset2Id, "name", "desc", 1, DataAssetUnitType.Time, 1000, 10000, new DataAssetRules(new DataAssetRule(1)), provider, state: DataAssetState.Published);
            dataAssetService.AddDiscovered(_asset2, _ndmPeer);
            _deposit2 = new Deposit(_deposit2Id, 1, 2, 3);
            _details2 = new DepositDetails(_deposit2, _asset2, Address.Zero, new byte[0], 1, new TransactionInfo[0], 2);

            _closed = new DataAsset(_closedId, "name", "desc", 1, DataAssetUnitType.Unit, 1000, 10000, new DataAssetRules(new DataAssetRule(1)), provider, state: DataAssetState.Closed);
            dataAssetService.AddDiscovered(_closed, _ndmPeer);
            _depositForClosed        = new Deposit(_depositForClosedId, 1, 2, 3);
            _depositForClosedDetails = new DepositDetails(_depositForClosed, _closed, Address.Zero, new byte[0], 1, new TransactionInfo[0]);

            _missingAsset             = new DataAsset(_missingAssetId, "name", "desc", 1, DataAssetUnitType.Unit, 1000, 10000, new DataAssetRules(new DataAssetRule(1)), provider, state: DataAssetState.Published);
            _depositForMissing        = new Deposit(_depositForMissingId, 1, 2, 3);
            _depositForMissingDetails = new DepositDetails(_depositForMissing, _missingAsset, Address.Zero, new byte[0], 1, new TransactionInfo[0]);

            IDepositProvider depositProvider = Substitute.For <IDepositProvider>();

            depositProvider.GetAsync(_deposit1Id).Returns(_details1);
            depositProvider.GetAsync(_deposit2Id).Returns(_details2);
            depositProvider.GetAsync(_depositForMissingId).Returns(_depositForMissingDetails);
            depositProvider.GetAsync(_depositForClosedId).Returns(_depositForClosedDetails);

            _ndmPeer = Substitute.For <INdmPeer>();
            _ndmPeer.ProviderAddress.Returns(_providerAddress);
            _ndmPeer.NodeId.Returns(_providerNodeId);

            _providerService = new ProviderService(providerRepository, notifier, LimboLogs.Instance);
            _providerService.Add(_ndmPeer);

            _sessionRepository = new ConsumerSessionInMemoryRepository();
            _sessionService    = new SessionService(_providerService, depositProvider, dataAssetService, _sessionRepository, Timestamper.Default, notifier, LimboLogs.Instance);
        }
예제 #3
0
        public async Task send_should_fail_if_active_session_does_not_exist(int fetchSessionRetries)
        {
            const int fetchSessionRetryDelayMilliseconds = 100;
            var       receipt = GetDataDeliveryReceiptRequest();
            var       deposit = GetDepositDetails();

            _depositProvider.GetAsync(receipt.DepositId).Returns(deposit);
            var stopwatch = new Stopwatch();

            stopwatch.Start();
            await _receiptService.SendAsync(receipt, fetchSessionRetries, fetchSessionRetryDelayMilliseconds);

            stopwatch.Stop();
            var expectedTime = 0.9 * fetchSessionRetryDelayMilliseconds * fetchSessionRetries;

            stopwatch.ElapsedMilliseconds.Should().BeGreaterOrEqualTo((long)expectedTime);
            await _depositProvider.Received().GetAsync(receipt.DepositId);

            _sessionService.Received(fetchSessionRetries + 1).GetActive(receipt.DepositId);
        }
예제 #4
0
        public async Task disable_data_stream_should_fail_for_locked_account()
        {
            var depositId = Keccak.Zero;
            var client    = "test";
            var session   = GetConsumerSession();
            var deposit   = GetDepositDetails();

            _sessionService.GetActive(depositId).Returns(session);
            _providerService.GetPeer(session.ProviderAddress).Returns(_providerPeer);
            _depositProvider.GetAsync(session.DepositId).Returns(deposit);

            var result = await _dataStreamService.DisableDataStreamAsync(depositId, client);

            result.Should().BeNull();
            _providerPeer.DidNotReceive().SendDisableDataStream(depositId, client);
            _sessionService.Received(1).GetActive(depositId);
            _providerService.Received(1).GetPeer(session.ProviderAddress);
            await _depositProvider.Received(1).GetAsync(session.DepositId);

            _wallet.Received(1).IsUnlocked(deposit.Consumer);
        }
예제 #5
0
        private async Task <(DepositDetails deposit, ConsumerSession session)> TryGetDepositAndSessionAsync(
            Keccak depositId, int fetchSessionRetries = 5, int fetchSessionRetryDelayMilliseconds = 3000)
        {
            var deposit = await _depositProvider.GetAsync(depositId);

            if (deposit is null)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Deposit: '{depositId}' was not found.");
                }

                return(null, null);
            }

            var session = _sessionService.GetActive(depositId);

            if (!(session is null))
            {
                return(deposit, session);
            }

            if (fetchSessionRetries <= 0)
            {
                return(deposit, null);
            }

            var retry = 0;

            while (retry < fetchSessionRetries)
            {
                retry++;
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Retrying ({retry}/{fetchSessionRetries}) fetching an active session for deposit: {deposit} in {fetchSessionRetryDelayMilliseconds} ms...");
                }
                await Task.Delay(fetchSessionRetryDelayMilliseconds);

                session = _sessionService.GetActive(depositId);
                if (session is null)
                {
                    continue;
                }
                if (_logger.IsInfo)
                {
                    _logger.Info($"Found an active session: '{session.Id}' for deposit: '{deposit.Id}'.");
                }
                break;
            }

            return((session is null) ? (deposit, null) : (deposit, session));
        }
        private async Task VerifyUnitsAsync(DataAssetUnitType unitType, uint paidUnits,
                                            Func <DepositDetails, Session, uint> expectedConsumedUnits,
                                            Func <DepositDetails, Session, uint> expectedUnpaidUnits)
        {
            var depositId = Keccak.Zero;
            var consumedUnitsFromProvider = 10u;
            var session = GetConsumerSession(paidUnits);
            var deposit = GetDepositDetails(unitType);

            _sessionService.GetActive(depositId).Returns(session);
            _depositProvider.GetAsync(session.DepositId).Returns(deposit);

            await _dataConsumerService.SetUnitsAsync(depositId, consumedUnitsFromProvider);

            session.ConsumedUnitsFromProvider.Should().Be(consumedUnitsFromProvider);
            session.ConsumedUnits.Should().Be(expectedConsumedUnits(deposit, session));
            session.UnpaidUnits.Should().Be(expectedUnpaidUnits(deposit, session));
            await _depositProvider.Received(1).GetAsync(depositId);

            _sessionService.Received(1).GetActive(depositId);
            await _sessionRepository.Received(1).UpdateAsync(session);
        }
예제 #7
0
        private async Task <(DepositDetails deposit, ConsumerSession session)> TryGetDepositAndSessionAsync(
            Keccak depositId)
        {
            var deposit = await _depositProvider.GetAsync(depositId);

            if (deposit is null)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Deposit: '{depositId}' was not found.");
                }

                return(null, null);
            }

            var session = _sessionService.GetActive(depositId);

            return((session is null) ? (deposit, null) : (deposit, session));
        }
        public async Task send_data_request_should_fail_for_locked_account()
        {
            var depositId = Keccak.Zero;
            var deposit   = GetDepositDetails();

            _depositProvider.GetAsync(depositId).Returns(deposit);

            var result = await _dataRequestService.SendAsync(depositId);

            result.Should().Be(DataRequestResult.ConsumerAccountLocked);
            await _depositProvider.Received(1).GetAsync(depositId);

            _wallet.Received(1).IsUnlocked(deposit.Consumer);
        }
예제 #9
0
        public async Task <DataRequestResult> SendAsync(Keccak depositId)
        {
            var deposit = await _depositProvider.GetAsync(depositId);

            if (deposit is null)
            {
                return(DataRequestResult.DepositNotFound);
            }

            if (!_wallet.IsUnlocked(deposit.Consumer))
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Account: '{deposit.Consumer}' is locked, can't send a data request.");
                }

                return(DataRequestResult.ConsumerAccountLocked);
            }

            if (deposit.DataAsset.KycRequired &&
                !(await _kycVerifier.IsVerifiedAsync(deposit.DataAsset.Id, deposit.Consumer)))
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Deposit with id: '{depositId}' has unconfirmed KYC.'");
                }

                return(DataRequestResult.KycUnconfirmed);
            }

            if (!deposit.Confirmed)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Deposit with id: '{depositId}' is unconfirmed.'");
                }

                return(DataRequestResult.DepositUnconfirmed);
            }

            if (deposit.IsExpired((uint)_timestamper.EpochSeconds))
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Deposit with id: '{depositId}' is expired.'");
                }

                return(DataRequestResult.DepositExpired);
            }

            var providerPeer = _providerService.GetPeer(deposit.DataAsset.Provider.Address);

            if (providerPeer is null)
            {
                return(DataRequestResult.ProviderNotFound);
            }

            var sessions = await _sessionRepository.BrowseAsync(new GetConsumerSessions
            {
                DepositId = depositId,
                Results   = int.MaxValue
            });

            var consumedUnits = sessions.Items.Any() ? (uint)sessions.Items.Sum(s => s.ConsumedUnits) : 0;
            var dataRequest   = CreateDataRequest(deposit);

            if (_logger.IsInfo)
            {
                _logger.Info($"Sending data request for deposit with id: '{depositId}', consumed units: {consumedUnits}, address: '{dataRequest.Consumer}'.");
            }
            var result = await providerPeer.SendDataRequestAsync(dataRequest, consumedUnits);

            if (_logger.IsInfo)
            {
                _logger.Info($"Received data request result: '{result}' for data asset: '{dataRequest.DataAssetId}', deposit: '{depositId}', consumed units: {consumedUnits}, address: '{dataRequest.Consumer}'.");
            }
            await _consumerNotifier.SendDataRequestResultAsync(depositId, result);

            return(result);
        }
예제 #10
0
        public async Task StartSessionAsync(Session session, INdmPeer provider)
        {
//            var providerPeer = _providerService.GetPeer(provider.ProviderAddress);
//            if (!_providers.TryGetValue(provider.NodeId, out var providerPeer))
//            {
//                if (_logger.IsWarn) _logger.Warn($"Cannot start the session: '{session.Id}', provider: '{provider.NodeId}' was not found.");
//
//                return;
//            }

            var deposit = await _depositProvider.GetAsync(session.DepositId);

            if (deposit is null)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Cannot start the session: '{session.Id}', deposit: '{session.DepositId}' was not found.");
                }

                return;
            }

            var dataAssetId = deposit.DataAsset.Id;
            var dataAsset   = _dataAssetService.GetDiscovered(dataAssetId);

            if (dataAsset is null)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Available data asset: '{dataAssetId}' was not found.");
                }

                return;
            }

            if (!_dataAssetService.IsAvailable(dataAsset))
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Data asset: '{dataAssetId}' is unavailable, state: {dataAsset.State}.");
                }

                return;
            }

            if (!provider.ProviderAddress.Equals(deposit.DataAsset.Provider.Address))
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Cannot start the session: '{session.Id}' for deposit: '{session.DepositId}', provider address (peer): '{provider.ProviderAddress}' doesn't equal the address from data asset: '{deposit.DataAsset.Provider.Address}'.");
                }

                return;
            }

            var sessions = await _sessionRepository.BrowseAsync(new GetConsumerSessions
            {
                DepositId = session.DepositId,
                Results   = int.MaxValue
            });

            var consumedUnits = sessions.Items.Any() ? (uint)sessions.Items.Sum(s => s.ConsumedUnits) : 0;

            if (_logger.IsInfo)
            {
                _logger.Info($"Starting the session: '{session.Id}' for deposit: '{session.DepositId}'. Settings consumed units - provider: {session.StartUnitsFromProvider}, consumer: {consumedUnits}.");
            }
            var consumerSession = ConsumerSession.From(session);

            consumerSession.Start(session.StartTimestamp);
            var previousSession = await _sessionRepository.GetPreviousAsync(consumerSession);

            var upfrontUnits = (uint)(deposit.DataAsset.Rules.UpfrontPayment?.Value ?? 0);

            if (upfrontUnits > 0 && previousSession is null)
            {
                consumerSession.AddUnpaidUnits(upfrontUnits);
                if (_logger.IsInfo)
                {
                    _logger.Info($"Unpaid units: {upfrontUnits} for session: '{session.Id}' based on upfront payment.");
                }
            }

            var unpaidUnits = previousSession?.UnpaidUnits ?? 0;

            if (unpaidUnits > 0 && !(previousSession is null))
            {
                consumerSession.AddUnpaidUnits(unpaidUnits);
                if (_logger.IsInfo)
                {
                    _logger.Info($"Unpaid units: {unpaidUnits} for session: '{session.Id}' from previous session: '{previousSession.Id}'.");
                }
            }

            if (deposit.DataAsset.UnitType == DataAssetUnitType.Time)
            {
                var unpaidTimeUnits = (uint)consumerSession.StartTimestamp - deposit.ConfirmationTimestamp;
                consumerSession.AddUnpaidUnits(unpaidTimeUnits);
                if (_logger.IsInfo)
                {
                    _logger.Info($"Unpaid units: '{unpaidTimeUnits}' for deposit: '{session.DepositId}' based on time.");
                }
            }

            SetActiveSession(consumerSession);
            await _sessionRepository.AddAsync(consumerSession);

            await _consumerNotifier.SendSessionStartedAsync(session.DepositId, session.Id);

            if (_logger.IsInfo)
            {
                _logger.Info($"Started a session with id: '{session.Id}' for deposit: '{session.DepositId}', address: '{deposit.Consumer}'.");
            }
        }
예제 #11
0
        public async Task StartSessionAsync(Session session, INdmPeer provider)
        {
            DepositDetails?deposit = await _depositProvider.GetAsync(session.DepositId);

            if (deposit is null)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Cannot start the session: '{session.Id}', deposit: '{session.DepositId}' was not found.");
                }
                return;
            }

            if (session.StartTimestamp < deposit.ConfirmationTimestamp)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Cannot start the session: '{session.Id}', session timestamp {session.StartTimestamp} is before deposit confirmation timestamp {deposit.ConfirmationTimestamp}.");
                }
                return;
            }

            Keccak dataAssetId = deposit.DataAsset.Id;

            if (dataAssetId != session.DataAssetId)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Inconsistent data - data asset ID on deposit is '{dataAssetId}' while on session is '{session.DataAssetId}'.");
                }
                return;
            }

            DataAsset?dataAsset = _dataAssetService.GetDiscovered(dataAssetId);

            if (dataAsset is null)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Available data asset: '{dataAssetId}' was not found.");
                }
                return;
            }

            if (!_dataAssetService.IsAvailable(dataAsset))
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Data asset: '{dataAssetId}' is unavailable, state: {dataAsset.State}.");
                }
                return;
            }

            if (session.ProviderAddress == null)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Session: '{session.Id}' for '{session.DepositId}' cannot be started because of the unknown provider address.");
                }
                return;
            }

            if (!provider.ProviderAddress !.Equals(deposit.DataAsset.Provider.Address))
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Cannot start the session: '{session.Id}' for deposit: '{session.DepositId}', provider address (peer): '{provider.ProviderAddress}' doesn't equal the address from data asset: '{deposit.DataAsset.Provider.Address}'.");
                }
                return;
            }

            PagedResult <ConsumerSession> sessions = await _sessionRepository.BrowseAsync(new GetConsumerSessions
            {
                DepositId = session.DepositId,
                Results   = int.MaxValue
            });

            uint consumedUnits = sessions.Items.Any() ? (uint)sessions.Items.Sum(s => s.ConsumedUnits) : 0;

            if (_logger.IsInfo)
            {
                _logger.Info($"Starting the session: '{session.Id}' for deposit: '{session.DepositId}'. Settings consumed units - provider: {session.StartUnitsFromProvider}, consumer: {consumedUnits}.");
            }
            ConsumerSession consumerSession = ConsumerSession.From(session);

            consumerSession.Start(session.StartTimestamp);
            ConsumerSession?previousSession = await _sessionRepository.GetPreviousAsync(consumerSession);

            uint upfrontUnits = (uint)(deposit.DataAsset.Rules.UpfrontPayment?.Value ?? 0);

            if (upfrontUnits > 0 && previousSession is null)
            {
                consumerSession.AddUnpaidUnits(upfrontUnits);
                if (_logger.IsInfo)
                {
                    _logger.Info($"Unpaid units: {upfrontUnits} for session: '{session.Id}' based on upfront payment.");
                }
            }

            uint unpaidUnits = previousSession?.UnpaidUnits ?? 0;

            if (unpaidUnits > 0 && !(previousSession is null))
            {
                consumerSession.AddUnpaidUnits(unpaidUnits);
                if (_logger.IsInfo)
                {
                    _logger.Info($"Unpaid units: {unpaidUnits} for session: '{session.Id}' from previous session: '{previousSession.Id}'.");
                }
            }

            if (deposit.DataAsset.UnitType == DataAssetUnitType.Time)
            {
                uint unpaidTimeUnits = (uint)consumerSession.StartTimestamp - deposit.ConfirmationTimestamp;
                consumerSession.AddUnpaidUnits(unpaidTimeUnits);
                if (_logger.IsInfo)
                {
                    _logger.Info($"Unpaid units: '{unpaidTimeUnits}' for deposit: '{session.DepositId}' based on time.");
                }
            }

            SetActiveSession(consumerSession);
            await _sessionRepository.AddAsync(consumerSession);

            await _consumerNotifier.SendSessionStartedAsync(session.DepositId, session.Id);

            if (_logger.IsInfo)
            {
                _logger.Info($"Started a session with id: '{session.Id}' for deposit: '{session.DepositId}', address: '{deposit.Consumer}'.");
            }
        }
예제 #12
0
        private async Task <Keccak?> ToggleDataStreamAsync(Keccak depositId, bool enable, string client, string[]?args = null)
        {
            ConsumerSession?session = _sessionService.GetActive(depositId);

            if (session is null)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Session for deposit: '{depositId}' was not found.");
                }
                return(null);
            }

            INdmPeer?provider = _providerService.GetPeer(session.ProviderAddress);

            if (provider is null)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Provider for address: '{session.ProviderAddress}' was not found.");
                }
                return(null);
            }

            DepositDetails?deposit = await _depositProvider.GetAsync(session.DepositId);

            if (deposit is null)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Cannot toggle data stream, deposit: '{session.DepositId}' was not found.");
                }

                return(null);
            }

            if (!_wallet.IsUnlocked(deposit.Consumer))
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Cannot toggle data stream for deposit: '{session.DepositId}', account: '{deposit.Consumer}' is locked.");
                }

                return(null);
            }

            Keccak    dataAssetId = deposit.DataAsset.Id;
            DataAsset?dataAsset   = _dataAssetService.GetDiscovered(dataAssetId);

            if (dataAsset is null)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Data asset: '{dataAssetId}' was not found.");
                }

                return(null);
            }

            if (!_dataAssetService.IsAvailable(dataAsset))
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Data asset: '{dataAssetId}' is unavailable, state: {dataAsset.State}.");
                }

                return(null);
            }

            if (!enable)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Sending disable data stream for deposit: '{depositId}', client: '{client}'.");
                }
                provider.SendDisableDataStream(depositId, client);

                return(depositId);
            }

            switch (dataAsset.QueryType)
            {
            case QueryType.Stream:
            {
                if (session.GetClient(client)?.StreamEnabled == true)
                {
                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Disabling an existing data stream for deposit: '{depositId}', client: '{client}'.");
                    }
                    provider.SendDisableDataStream(depositId, client);
                }

                if (_logger.IsInfo)
                {
                    _logger.Info($"Sending enable data stream for deposit: '{depositId}', client: '{client}'.");
                }
                break;
            }

            case QueryType.Query:
            {
                Metrics.SentQueries++;
                if (_logger.IsInfo)
                {
                    _logger.Info($"Sending the data query for deposit: '{depositId}', client: '{client}'.");
                }
                break;
            }

            default:
            {
                throw new InvalidOperationException($"Not supported data asset type: {dataAsset.QueryType}.");
            }
            }

            provider.SendEnableDataStream(depositId, client, args ?? Array.Empty <string>());

            return(depositId);
        }