private bool IsPaymentClaimCorrect(PaymentClaim paymentClaim)
        {
            var from = paymentClaim.UnitsRange.From;
            var to   = paymentClaim.UnitsRange.To;

            if (from > to)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Invalid units range for transaction. Units range: [{from},{to}]");
                }
                return(false);
            }

            if (to >= paymentClaim.Units)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Invalid units range for transaction. UnitsRange.To cannot be higher or equal to payment claim units. UnitRange: [{from},{to}], Units: {paymentClaim.Units}");
                }
                return(false);
            }

            return(true);
        }
        public InMemoryDepositNodesHandler(Keccak depositId, Address consumer, DataAssetUnitType unitType,
                                           uint verificationTimestamp, uint purchasedUnits, UInt256 unitPrice, uint consumedUnits,
                                           uint unpaidUnits, uint unmergedUnits, uint unclaimedUnits, uint graceUnits, uint expiryTime,
                                           PaymentClaim latestPaymentClaim, IEnumerable <DataDeliveryReceiptDetails> receipts,
                                           uint latestReceiptRequestNumber)
        {
            DepositId             = depositId;
            Consumer              = consumer;
            UnitType              = unitType;
            VerificationTimestamp = verificationTimestamp;
            PurchasedUnits        = purchasedUnits;
            UnitPrice             = unitPrice;
            _consumedUnits        = consumedUnits;
            _unpaidUnits          = unpaidUnits;
            _unmergedUnits        = unmergedUnits;
            _unclaimedUnits       = unclaimedUnits;
            _graceUnits           = graceUnits;
            ExpiryTime            = expiryTime;
            _latestPaymentClaim   = latestPaymentClaim;
            _receipts             = new ConcurrentStack <DataDeliveryReceiptDetails>(
                receipts ?? Enumerable.Empty <DataDeliveryReceiptDetails>());
            _latestReceiptRequestNumber = latestReceiptRequestNumber;
            var latestReceipt       = _receipts.Where(r => !r.IsMerged).OrderBy(r => r.Number).LastOrDefault();
            var latestMergedReceipt = _receipts.Where(r => r.IsMerged).OrderBy(r => r.Number).LastOrDefault();

            SetLatestReceipt(latestReceipt);
            SetLatestReceipt(latestMergedReceipt);
        }
Exemplo n.º 3
0
 public async Task Can_add_async()
 {
     IMongoDatabase database     = MongoForTest.Provider.GetDatabase();
     var            repo         = new PaymentClaimMongoRepository(database);
     PaymentClaim   paymentClaim = BuildDummyPaymentClaim();
     await repo.AddAsync(paymentClaim);
 }
Exemplo n.º 4
0
        private Task AddOrUpdateAsync(PaymentClaim paymentClaim)
        {
            var rlp = RlpObjectDecoder.Encode(paymentClaim);

            _database.Set(paymentClaim.Id, rlp.Bytes);

            return(Task.CompletedTask);
        }
Exemplo n.º 5
0
        public async Task Can_get_payment_summary()
        {
            IMongoDatabase database     = MongoForTest.Provider.GetDatabase();
            var            repo         = new PaymentClaimMongoRepository(database);
            PaymentClaim   paymentClaim = BuildDummyPaymentClaim();
            await repo.AddAsync(paymentClaim);

            await repo.GetPaymentsSummary(null, null, null);
        }
Exemplo n.º 6
0
        public async Task Can_get_by_id()
        {
            IMongoDatabase database     = MongoForTest.Provider.GetDatabase();
            var            repo         = new PaymentClaimMongoRepository(database);
            PaymentClaim   paymentClaim = BuildDummyPaymentClaim();
            await repo.AddAsync(paymentClaim);

            PaymentClaim result = await repo.GetAsync(paymentClaim.Id);

            result.Should().BeEquivalentTo(paymentClaim);
        }
Exemplo n.º 7
0
        private async Task <(UpdatedTransactionStatus status, PaymentClaim?)> TryGetPaymentClaimAsync(Keccak paymentClaimId, string method)
        {
            PaymentClaim paymentClaim = await _paymentClaimRepository.GetAsync(paymentClaimId);

            if (paymentClaim is null)
            {
                if (_logger.IsError)
                {
                    _logger.Error($"Payment claim with id: '{paymentClaimId}' was not found.");
                }
                return(UpdatedTransactionStatus.ResourceNotFound, null);
            }

            if (paymentClaim.Transaction is null)
            {
                if (_logger.IsError)
                {
                    _logger.Error($"Payment claim with id: '{paymentClaimId}' has no transaction.");
                }
                return(UpdatedTransactionStatus.MissingTransaction, null);
            }

            switch (paymentClaim.Status)
            {
            case PaymentClaimStatus.Sent:

                return(UpdatedTransactionStatus.Ok, paymentClaim);

            case PaymentClaimStatus.Rejected:
                if (_logger.IsError)
                {
                    LogError();
                }
                return(UpdatedTransactionStatus.ResourceRejected, null);

            case PaymentClaimStatus.Cancelled:
                if (_logger.IsError)
                {
                    LogError();
                }
                return(UpdatedTransactionStatus.ResourceCancelled, null);

            default:
                if (_logger.IsError)
                {
                    LogError();
                }
                return(UpdatedTransactionStatus.AlreadyIncluded, null);

                void LogError() => _logger.Error($"Cannot {method} for payment claim with id: '{paymentClaimId}' (status: '{paymentClaim.Status}').");
            }
        }
Exemplo n.º 8
0
        public async Task will_not_claim_without_enough_confirmations()
        {
            PaymentClaim claim = GenerateTestClaim(PaymentClaimStatus.Sent, "will_not_claim_without_enough_confirmations");

            await paymentClaimRepository.AddAsync(claim);

            var testTransaction = new NdmTransaction(new Transaction(), false, 1, Keccak.OfAnEmptyString, 5);

            blockchainBridge.GetTransactionAsync(claim.Transaction.Hash).Returns(Task.FromResult(testTransaction));

            transactionVerifier.VerifyAsync(testTransaction).Returns(Task.FromResult(new TransactionVerifierResult(true, 2, 5)));

            WaitForPaymentClaimsProcessing();

            var addedClaim = await paymentClaimRepository.GetAsync(claim.Id);

            Assert.IsTrue(addedClaim.Status != PaymentClaimStatus.Claimed);
        }
Exemplo n.º 9
0
        public async Task can_claim_payment()
        {
            PaymentClaim claim = GenerateTestClaim(PaymentClaimStatus.Sent, "can_claim_payment");

            await paymentClaimRepository.AddAsync(claim);

            var testTransaction = new NdmTransaction(new Transaction(), false, 5, Keccak.OfAnEmptyString, 1);

            blockchainBridge.GetTransactionAsync(claim.Transaction.Hash).Returns(Task.FromResult(testTransaction));

            transactionVerifier.VerifyAsync(testTransaction).Returns(Task.FromResult(new TransactionVerifierResult(true, 10, 2)));

            WaitForPaymentClaimsProcessing();

            var addedClaim = await paymentClaimRepository.GetAsync(claim.Id);

            Assert.IsTrue(addedClaim.Status == PaymentClaimStatus.Claimed);
        }
Exemplo n.º 10
0
        private PaymentClaim[] GetAll()
        {
            var paymentClaimsBytes = _database.GetAllValues().ToArray();

            if (paymentClaimsBytes.Length == 0)
            {
                return(Array.Empty <PaymentClaim>());
            }

            var paymentClaims = new PaymentClaim[paymentClaimsBytes.Length];

            for (var i = 0; i < paymentClaimsBytes.Length; i++)
            {
                paymentClaims[i] = Decode(paymentClaimsBytes[i]);
            }

            return(paymentClaims);
        }
        public async Task <Keccak?> SendTransactionAsync(PaymentClaim paymentClaim, UInt256 gasPrice)
        {
            bool isPaymentClaimCorrect = IsPaymentClaimCorrect(paymentClaim);

            if (!isPaymentClaimCorrect)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Payment claim id: {paymentClaim.Id} was incorrect. Claim will be rejected");
                }

                paymentClaim.Reject();
                await _paymentClaimRepository.UpdateAsync(paymentClaim);

                return(null);
            }

            Keccak     depositId       = paymentClaim.DepositId;
            UnitsRange range           = paymentClaim.UnitsRange;
            Keccak?    transactionHash = await _paymentService.ClaimPaymentAsync(paymentClaim, _coldWalletAddress, gasPrice);

            bool isTransactionHashValid = !(transactionHash is null) && transactionHash != Keccak.Zero;

            if (isTransactionHashValid)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Received a transaction hash: {transactionHash} for payment claim (id: '{paymentClaim.Id}') for deposit: '{depositId}', range: [{range.From}, {range.To}], units: {paymentClaim.Units}.");
                }
                return(transactionHash);
            }

            if (_logger.IsError)
            {
                _logger.Error($"There was an error when claiming a payment (id: '{paymentClaim.Id}') for deposit: '{depositId}', range: [{range.From}, {range.To}] with receipt [{range.From}, {range.To}], units: {paymentClaim.Units} - returned transaction is empty.");
            }

            return(null);
        }
Exemplo n.º 12
0
        public async Task <Keccak?> ClaimPaymentAsync(PaymentClaim paymentClaim, Address coldWalletAddress,
                                                      UInt256 gasPrice)
        {
            byte[] txData = _abiEncoder.Encode(AbiEncodingStyle.IncludeSignature,
                                               ContractData.ClaimPaymentAbiSig,
                                               paymentClaim.AssetId.Bytes,
                                               paymentClaim.Units,
                                               paymentClaim.Value,
                                               paymentClaim.ExpiryTime,
                                               paymentClaim.Pepper,
                                               coldWalletAddress,
                                               paymentClaim.Consumer,
                                               new[] { paymentClaim.UnitsRange.From, paymentClaim.UnitsRange.To },
                                               paymentClaim.Signature.V,
                                               paymentClaim.Signature.R,
                                               paymentClaim.Signature.S);

            if (_logger.IsInfo)
            {
                _logger.Info($"Sending a payment claim transaction - Range: [{paymentClaim.UnitsRange.From},{paymentClaim.UnitsRange.To}] Units: {paymentClaim.Units} to be paid out to {coldWalletAddress}");
            }

            Transaction transaction = new Transaction
            {
                Value         = 0,
                Data          = txData,
                To            = _contractAddress,
                SenderAddress = paymentClaim.Provider,
                GasLimit      = (long)GasLimit, // when account does not exist then we pay for account creation of cold wallet
                GasPrice      = gasPrice,
                Nonce         = await _bridge.GetNonceAsync(paymentClaim.Provider)
            };

            // check
            _wallet.Sign(transaction, await _bridge.GetNetworkIdAsync());

            return(await _bridge.SendOwnTransactionAsync(transaction));
        }
Exemplo n.º 13
0
        private static PaymentClaim BuildDummyPaymentClaim()
        {
            PaymentClaim paymentClaim = new PaymentClaim(
                TestItem.KeccakA,
                TestItem.KeccakB,
                TestItem.KeccakC,
                "asset_name",
                1,
                2,
                new UnitsRange(1, 2),
                4,
                5,
                6,
                new byte[] { 1, 2, 3 },
                TestItem.AddressA,
                TestItem.AddressB,
                new Signature(new byte[65]),
                7,
                Enumerable.Empty <TransactionInfo>(),
                PaymentClaimStatus.Claimed);

            return(paymentClaim);
        }
Exemplo n.º 14
0
 public PaymentClaimForRpc(PaymentClaim paymentClaim)
 {
     Id           = paymentClaim.Id;
     DepositId    = paymentClaim.DepositId;
     AssetId      = paymentClaim.AssetId;
     AssetName    = paymentClaim.AssetName;
     Units        = paymentClaim.Units;
     ClaimedUnits = paymentClaim.ClaimedUnits;
     UnitsRange   = new UnitsRangeForRpc(paymentClaim.UnitsRange);
     Value        = paymentClaim.Value;
     ClaimedValue = paymentClaim.ClaimedValue;
     ExpiryTime   = paymentClaim.ExpiryTime;
     Provider     = paymentClaim.Provider;
     Consumer     = paymentClaim.Consumer;
     Transaction  = paymentClaim.Transaction is null ? null : new TransactionInfoForRpc(paymentClaim.Transaction);
     Transactions = paymentClaim.Transactions?.Select(t => new TransactionInfoForRpc(t))
                    .OrderBy(t => t.Timestamp) ??
                    Enumerable.Empty <TransactionInfoForRpc>();
     TransactionCost = paymentClaim.TransactionCost;
     Income          = paymentClaim.Income;
     Timestamp       = paymentClaim.Timestamp;
     Status          = paymentClaim.Status.ToString().ToLowerInvariant();
 }
 public IDepositNodesHandler CreateInMemory(Keccak depositId, Address consumer, DataAssetUnitType unitType,
                                            uint verificationTimestamp, uint purchasedUnits, UInt256 unitPrice, uint consumedUnits, uint unpaidUnits,
                                            uint unmergedUnits, uint unclaimedUnits, uint graceUnits, uint expiryTime, PaymentClaim latestPaymentClaim,
                                            IEnumerable <DataDeliveryReceiptDetails> latestReceipts, uint latestReceiptRequestNumber)
 => new InMemoryDepositNodesHandler(depositId, consumer, unitType, verificationTimestamp,
                                    purchasedUnits, unitPrice, consumedUnits, unpaidUnits, unmergedUnits, unclaimedUnits, graceUnits,
                                    expiryTime, latestPaymentClaim, latestReceipts, latestReceiptRequestNumber);
Exemplo n.º 16
0
        public async Task <IDepositNodesHandler> InitAsync(Keccak depositId, uint unpaidSessionUnits = 0)
        {
            if (_logger.IsInfo)
            {
                _logger.Info($"Initializing deposit: '{depositId}'.");
            }

            Consumer?consumer = await _consumerRepository.GetAsync(depositId);

            if (consumer is null)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Consumer has not been found for deposit: '{depositId}'.");
                }

                return(null);
            }

            if (_depositNodesHandlers.TryGetValue(depositId, out IDepositNodesHandler? deposit))
            {
                deposit.AddUnpaidUnits(unpaidSessionUnits);
                deposit.AddUnmergedUnits(unpaidSessionUnits);
                deposit.AddUnclaimedUnits(unpaidSessionUnits);

                if (_logger.IsInfo)
                {
                    _logger.Info($"Updated deposit: '{depositId}'.");
                }

                if (_logger.IsInfo)
                {
                    _logger.Info($"Deposit: '{depositId}' has {deposit.UnpaidUnits} unpaid units, unmerged: {deposit.UnmergedUnits}, unclaimed: {deposit.UnclaimedUnits}, grace: {deposit.GraceUnits}.");
                }

                return(deposit);
            }
            else
            {
                uint    purchasedUnits = consumer.DataRequest.Units;
                UInt256 unitPrice      = consumer.DataAsset.UnitPrice;
                var     sessions       = await _sessionRepository.BrowseAsync(new GetProviderSessions
                {
                    DepositId = depositId,
                    Results   = int.MaxValue
                });

                var depositReceipts = await _receiptRepository.BrowseAsync(depositId);

                var receipts = depositReceipts.OrderBy(r => r.Timestamp)
                               .ThenBy(r => r.Request.UnitsRange.To)
                               .ThenByDescending(r => r.Request.UnitsRange.From)
                               .ToArray();

                uint consumedUnits = (uint)sessions.Items.Sum(s => s.ConsumedUnits);
                uint graceUnits    = (uint)sessions.Items.Sum(s => s.GraceUnits);
                uint unpaidUnits   = (uint)(sessions.Items.Sum(s => s.UnpaidUnits) -
                                            sessions.Items.Sum(s => s.SettledUnits));

                ulong latestMergedReceiptTimestamp = receipts.LastOrDefault(r => r.IsMerged)?.Timestamp ?? 0;

                uint unmergedUnits = (uint)receipts.Where(r => !r.IsClaimed && !r.IsMerged &&
                                                          r.Timestamp >= latestMergedReceiptTimestamp)
                                     .Sum(r => r.Request.UnitsRange.To - r.Request.UnitsRange.From + 1);

                ulong latestClaimedReceiptTimestamp = receipts.LastOrDefault(r => r.IsClaimed)?.Timestamp ?? 0;

                uint unclaimedUnits = (uint)receipts.Where(r => !r.IsClaimed && !r.IsMerged &&
                                                           r.Timestamp >= latestClaimedReceiptTimestamp)
                                      .Sum(r => r.Request.UnitsRange.To - r.Request.UnitsRange.From + 1);

                var  latestReceipts             = receipts.Where(r => r.Timestamp >= latestClaimedReceiptTimestamp);
                uint latestReceiptRequestNumber = receipts.Any() ? receipts.Max(r => r.Number) : 0;
                var  paymentClaimsResult        = await _paymentClaimRepository.BrowseAsync(new GetPaymentClaims
                {
                    DepositId = depositId,
                    Page      = 1,
                    Results   = int.MaxValue
                });

                PaymentClaim latestPaymentClaim = paymentClaimsResult.Items.OrderBy(c => c.Timestamp)
                                                  .ThenBy(c => c.UnitsRange.To).LastOrDefault();

                deposit = _depositNodesHandlerFactory.CreateInMemory(depositId, consumer.DataRequest.Consumer,
                                                                     consumer.DataAsset.UnitType, consumer.VerificationTimestamp, purchasedUnits, unitPrice,
                                                                     consumedUnits, unpaidUnits, unmergedUnits, unclaimedUnits, graceUnits,
                                                                     consumer.DataRequest.ExpiryTime, latestPaymentClaim, latestReceipts, latestReceiptRequestNumber);
                _depositNodesHandlers.TryAdd(depositId, deposit);
                if (_logger.IsInfo)
                {
                    _logger.Info($"Initialized deposit: '{depositId}'.");
                }

                if (_logger.IsInfo)
                {
                    _logger.Info($"Deposit: '{depositId}' has {deposit.UnpaidUnits} unpaid units, unmerged: {deposit.UnmergedUnits}, unclaimed: {deposit.UnclaimedUnits}, grace: {deposit.GraceUnits}.");
                }

                return(deposit);
            }
        }
Exemplo n.º 17
0
 public Task <Keccak?> SendTransactionAsync(PaymentClaim paymentClaim, UInt256 gasPrice)
 => _processor.SendTransactionAsync(paymentClaim, gasPrice);
Exemplo n.º 18
0
 public Task UpdateAsync(PaymentClaim paymentClaim) => AddOrUpdateAsync(paymentClaim);
        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 void SetLatestPaymentClaim(PaymentClaim claim) =>
 Interlocked.Exchange(ref _latestPaymentClaim, claim);