示例#1
0
        public async Task get_transaction_should_return_null_if_receipt_or_transaction_is_null()
        {
            var hash   = TestItem.KeccakA;
            var result = await _ndmBridge.GetTransactionAsync(hash);

            result.Should().BeNull();
            _blockchainBridge.Received().GetTransaction(hash);
        }
        public async Task get_transaction_should_invoke_proxy_eth_getTransactionByHash_and_eth_getTransactionReceipt_and_return_null_if_receipt_or_transaction_is_null()
        {
            var hash   = TestItem.KeccakA;
            var result = await _ndmBridge.GetTransactionAsync(hash);

            await _proxy.Received().eth_getTransactionByHash(hash);

            await _proxy.Received().eth_getTransactionReceipt(hash);

            result.Should().BeNull();
        }
示例#3
0
        public void update_gas_price_should_fail_if_sending_new_transaction_fails()
        {
            var transactionHash = TestItem.KeccakA;
            var transaction     = Build.A.Transaction.TestObject;

            _blockchainBridge.GetTransactionAsync(transactionHash)
            .Returns(new NdmTransaction(transaction, true, 1, TestItem.KeccakB, 1));
            Func <Task> act = () => _transactionService.UpdateGasPriceAsync(transactionHash, 1);

            act.Should().Throw <InvalidOperationException>()
            .WithMessage("Transaction was not sent (received an empty hash).");
            _blockchainBridge.Received().GetTransactionAsync(transactionHash);
            _blockchainBridge.Received().GetNetworkIdAsync();
            _blockchainBridge.Received().SendOwnTransactionAsync(transaction);
        }
示例#4
0
        public async Task try_confirm_should_not_confirm_deposit_if_transaction_is_pending()
        {
            var deposit     = GetDepositDetails();
            var transaction = GetTransaction(true);

            _blockchainBridge.GetTransactionAsync(deposit.Transaction.Hash).Returns(transaction);
            await _depositConfirmationService.TryConfirmAsync(deposit);

            deposit.Confirmed.Should().BeFalse();
            deposit.Transaction.State.Should().Be(TransactionState.Pending);
            await _blockchainBridge.Received().GetTransactionAsync(deposit.Transaction.Hash);
        }
        public async Task try_confirm_should_skip_further_processing_if_block_was_not_found()
        {
            const int latestBlockNumber = 3;
            var       deposit           = GetDepositDetails();
            var       transaction       = GetTransaction();

            _blockchainBridge.GetTransactionAsync(deposit.TransactionHash).Returns(transaction);
            _blockchainBridge.GetLatestBlockNumberAsync().Returns(latestBlockNumber);
            await _depositConfirmationService.TryConfirmAsync(deposit);

            await _blockchainBridge.Received().GetLatestBlockNumberAsync();

            await _blockchainBridge.Received().FindBlockAsync(latestBlockNumber);

            await _depositService.DidNotReceive().VerifyDepositAsync(deposit.Consumer, deposit.Id, Arg.Any <long>());
        }
示例#6
0
        public async Task can_send_transaction_when_claim_status_unknown()
        {
            PaymentClaim claim = GenerateTestClaim(PaymentClaimStatus.Unknown, "can_send_transaction_when_claim_status_unknown");

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

            await paymentClaimRepository.AddAsync(claim);

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

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

            WaitForPaymentClaimsProcessing();

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

            Assert.IsTrue(addedClaim.Status == PaymentClaimStatus.Sent);
        }
        public async Task TryConfirmAsync(DepositDetails deposit)
        {
            if (deposit.Confirmed || deposit.Rejected)
            {
                return;
            }

            var transactionHash = deposit.TransactionHash;
            var transaction     = await _blockchainBridge.GetTransactionAsync(deposit.TransactionHash);

            if (transaction is null)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Transaction was not found for hash: '{transactionHash}' for deposit: '{deposit.Id}' to be confirmed.");
                }
                return;
            }

            var headNumber = await _blockchainBridge.GetLatestBlockNumberAsync();

            var(confirmations, rejected) = await VerifyDepositConfirmationsAsync(deposit, transaction, headNumber);

            if (rejected)
            {
                deposit.Reject();
                await _depositRepository.UpdateAsync(deposit);

                await _consumerNotifier.SendDepositRejectedAsync(deposit.Id);

                return;
            }

            if (_logger.IsInfo)
            {
                _logger.Info($"Deposit: '{deposit.Id}' has {confirmations} confirmations (required at least {_requiredBlockConfirmations}) for transaction hash: '{transactionHash}' to be confirmed.");
            }
            var confirmed = confirmations >= _requiredBlockConfirmations;

            if (confirmed)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Deposit with id: '{deposit.Deposit.Id}' has been confirmed.");
                }
            }

            if (confirmations != deposit.Confirmations || confirmed)
            {
                deposit.SetConfirmations(confirmations);
                await _depositRepository.UpdateAsync(deposit);
            }

            await _consumerNotifier.SendDepositConfirmationsStatusAsync(deposit.Id, deposit.DataAsset.Name,
                                                                        confirmations, _requiredBlockConfirmations, deposit.ConfirmationTimestamp, confirmed);
        }
        public async Task try_confirm_should_skip_further_processing_if_transaction_is_pending()
        {
            var deposit     = GetDepositDetails();
            var transaction = GetTransaction(true);

            _blockchainBridge.GetTransactionAsync(deposit.Transaction.Hash).Returns(transaction);
            await _depositConfirmationService.TryConfirmAsync(deposit);

            await _blockchainBridge.Received().GetTransactionAsync(deposit.Transaction.Hash);

            await _depositService.DidNotReceive().VerifyDepositAsync(deposit.Consumer, deposit.Id, Arg.Any <long>());
        }
示例#9
0
        private async Task TryConfirmClaimAsync(DepositDetails deposit, string type)
        {
            var depositId       = deposit.Id;
            var transactionHash = deposit.TransactionHash;
            var transaction     = await _blockchainBridge.GetTransactionAsync(transactionHash);

            if (transaction is null)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Transaction was not found for hash: '{transactionHash}' for deposit: '{depositId}' to claim the {type}refund.");
                }
                return;
            }

            if (_logger.IsInfo)
            {
                _logger.Info($"Trying to claim the {type}refund (transaction hash: '{transactionHash}') for deposit: '{depositId}'.");
            }
            var verifierResult = await _transactionVerifier.VerifyAsync(transaction);

            if (!verifierResult.BlockFound)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Block number: {transaction.BlockNumber}, hash: '{transaction.BlockHash}' was not found for transaction hash: '{transactionHash}' - {type}refund claim for deposit: '{depositId}' will not confirmed.");
                }
                return;
            }

            if (_logger.IsInfo)
            {
                _logger.Info($"The {type}refund claim (transaction hash: '{transactionHash}') for deposit: '{depositId}' has {verifierResult.Confirmations} confirmations (required at least {verifierResult.RequiredConfirmations}).");
            }
            if (!verifierResult.Confirmed)
            {
                return;
            }

            deposit.SetRefundClaimed();
            await _depositRepository.UpdateAsync(deposit);

            if (_logger.IsInfo)
            {
                _logger.Info($"The {type}refund claim (transaction hash: '{transactionHash}') for deposit: '{depositId}' has been confirmed.");
            }
        }
        public void update_gas_price_should_fail_if_transaction_is_not_pending()
        {
            var transactionHash = TestItem.KeccakA;
            var transaction     = Build.A.Transaction.TestObject;

            _blockchainBridge.GetTransactionAsync(transactionHash)
            .Returns(new NdmTransaction(transaction, false, 1, TestItem.KeccakB, 1));
            Func <Task> act = () => _transactionService.UpdateGasPriceAsync(transactionHash, 1);

            act.Should().Throw <InvalidOperationException>()
            .WithMessage($"Transaction with hash: '{transactionHash}' is not pending.");
            _blockchainBridge.Received().GetTransactionAsync(transactionHash);
        }
        private async Task <Keccak> UpdateAsync(Keccak transactionHash, Action <Transaction> update)
        {
            if (transactionHash is null)
            {
                throw new ArgumentException("Transaction hash cannot be null.", nameof(transactionHash));
            }

            var transactionDetails = await _blockchainBridge.GetTransactionAsync(transactionHash);

            if (transactionDetails is null)
            {
                throw new ArgumentException($"Transaction was not found for hash: '{transactionHash}'.", nameof(transactionHash));
            }

            if (!transactionDetails.IsPending)
            {
                throw new InvalidOperationException($"Transaction with hash: '{transactionHash}' is not pending.");
            }

            var transaction = transactionDetails.Transaction;

            update(transaction);
            _wallet.Sign(transaction, await _blockchainBridge.GetNetworkIdAsync());
            var hash = await _blockchainBridge.SendOwnTransactionAsync(transaction);

            if (hash is null)
            {
                throw new InvalidOperationException("Transaction was not sent (received an empty hash).");
            }

            if (_logger.IsInfo)
            {
                _logger.Info($"Received a new transaction hash: '{hash}' (previous transaction hash: '{transactionHash}').");
            }

            return(hash);
        }
示例#12
0
        public async Task TryConfirmAsync(DepositDetails deposit)
        {
            if (deposit.Confirmed || deposit.Rejected || deposit.Cancelled || deposit.Transaction is null)
            {
                return;
            }

            NdmTransaction? transactionDetails  = null;
            TransactionInfo includedTransaction = deposit.Transactions.SingleOrDefault(t => t.State == TransactionState.Included);
            IOrderedEnumerable <TransactionInfo> pendingTransactions = deposit.Transactions
                                                                       .Where(t => t.State == TransactionState.Pending)
                                                                       .OrderBy(t => t.Timestamp);

            if (_logger.IsInfo)
            {
                _logger.Info($"Deposit: '{deposit.Id}' pending transactions: {string.Join(", ", pendingTransactions.Select(t => $"{t.Hash} [{t.Type}]"))}");
            }

            if (includedTransaction is null)
            {
                foreach (TransactionInfo transaction in pendingTransactions)
                {
                    Keccak?transactionHash = transaction.Hash;
                    if (transactionHash is null)
                    {
                        if (_logger.IsInfo)
                        {
                            _logger.Info($"Transaction was not found for hash: '{null}' for deposit: '{deposit.Id}' to be confirmed.");
                        }
                        continue;
                    }

                    transactionDetails = await _blockchainBridge.GetTransactionAsync(transactionHash);

                    if (transactionDetails is null)
                    {
                        if (_logger.IsInfo)
                        {
                            _logger.Info($"Transaction was not found for hash: '{transactionHash}' for deposit: '{deposit.Id}' to be confirmed.");
                        }
                        continue;
                    }

                    if (transactionDetails.IsPending)
                    {
                        if (_logger.IsInfo)
                        {
                            _logger.Info($"Transaction with hash: '{transactionHash}' for deposit: '{deposit.Id}' is still pending.");
                        }
                        continue;
                    }

                    deposit.SetIncludedTransaction(transactionHash);
                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Transaction with hash: '{transactionHash}', type: '{transaction.Type}' for deposit: '{deposit.Id}' was included into block: {transactionDetails.BlockNumber}.");
                    }
                    await _depositRepository.UpdateAsync(deposit);

                    includedTransaction = transaction;
                    break;
                }
            }
            else if (includedTransaction.Type == TransactionType.Cancellation)
            {
                return;
            }
            else
            {
                transactionDetails = includedTransaction.Hash == null ? null : await _blockchainBridge.GetTransactionAsync(includedTransaction.Hash);

                if (transactionDetails is null)
                {
                    if (_logger.IsWarn)
                    {
                        _logger.Warn($"Transaction (set as included) was not found for hash: '{includedTransaction.Hash}' for deposit: '{deposit.Id}'.");
                    }
                    return;
                }
            }

            if (includedTransaction is null)
            {
                return;
            }

            long headNumber = await _blockchainBridge.GetLatestBlockNumberAsync();

            (uint confirmations, bool rejected) = await VerifyDepositConfirmationsAsync(deposit, transactionDetails !, headNumber);

            if (rejected)
            {
                deposit.Reject();
                await _depositRepository.UpdateAsync(deposit);

                await _consumerNotifier.SendDepositRejectedAsync(deposit.Id);

                return;
            }

            if (_logger.IsInfo)
            {
                _logger.Info($"Deposit: '{deposit.Id}' has {confirmations} confirmations (required at least {_requiredBlockConfirmations}) for transaction hash: '{includedTransaction.Hash}' to be confirmed.");
            }
            bool confirmed = confirmations >= _requiredBlockConfirmations;

            if (confirmed)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Deposit with id: '{deposit.Deposit.Id}' has been confirmed.");
                }
            }

            if (confirmations != deposit.Confirmations || confirmed)
            {
                deposit.SetConfirmations(confirmations);
                await _depositRepository.UpdateAsync(deposit);
            }

            await _consumerNotifier.SendDepositConfirmationsStatusAsync(deposit.Id, deposit.DataAsset.Name,
                                                                        confirmations, _requiredBlockConfirmations, deposit.ConfirmationTimestamp, confirmed);
        }
示例#13
0
        private async Task <bool> TryConfirmClaimAsync(DepositDetails deposit, string type)
        {
            var claimType       = $"{type}refund";
            var depositId       = deposit.Id;
            var transactionHash = deposit.ClaimedRefundTransaction.Hash;
            var transaction     = await _blockchainBridge.GetTransactionAsync(transactionHash);

            if (transaction is null)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Transaction was not found for hash: '{transactionHash}' for deposit: '{depositId}' to claim the {claimType}.");
                }
                return(false);
            }

            if (transaction.IsPending)
            {
                if (_logger.IsInfo)
                {
                    _logger.Info($"Transaction with hash: '{transactionHash}' for deposit: '{deposit.Id}' ({claimType}) is still pending.");
                }
                return(false);
            }

            if (deposit.ClaimedRefundTransaction.State == TransactionState.Pending)
            {
                deposit.ClaimedRefundTransaction.SetIncluded();
                if (_logger.IsInfo)
                {
                    _logger.Info($"Transaction with hash: '{transactionHash}' for deposit: '{deposit.Id}' ({claimType}) was included into block: {transaction.BlockNumber}.");
                }
                await _depositRepository.UpdateAsync(deposit);
            }

            if (_logger.IsInfo)
            {
                _logger.Info($"Trying to claim the {claimType} (transaction hash: '{transactionHash}') for deposit: '{depositId}'.");
            }
            var verifierResult = await _transactionVerifier.VerifyAsync(transaction);

            if (!verifierResult.BlockFound)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Block number: {transaction.BlockNumber}, hash: '{transaction.BlockHash}' was not found for transaction hash: '{transactionHash}' - {claimType} claim for deposit: '{depositId}' will not confirmed.");
                }
                return(false);
            }

            if (_logger.IsInfo)
            {
                _logger.Info($"The {claimType} claim (transaction hash: '{transactionHash}') for deposit: '{depositId}' has {verifierResult.Confirmations} confirmations (required at least {verifierResult.RequiredConfirmations}).");
            }
            if (!verifierResult.Confirmed)
            {
                return(false);
            }

            deposit.SetRefundClaimed();
            await _depositRepository.UpdateAsync(deposit);

            if (_logger.IsInfo)
            {
                _logger.Info($"The {claimType} claim (transaction hash: '{transactionHash}') for deposit: '{depositId}' has been confirmed.");
            }

            return(true);
        }
示例#14
0
        private async Task <bool> TryConfirmClaimAsync(DepositDetails deposit, string type)
        {
            string claimType = $"{type}refund";
            Keccak depositId = deposit.Id;

            NdmTransaction? transactionDetails  = null;
            TransactionInfo includedTransaction = deposit.Transactions.SingleOrDefault(t => t.State == TransactionState.Included);
            IOrderedEnumerable <TransactionInfo> pendingTransactions = deposit.Transactions
                                                                       .Where(t => t.State == TransactionState.Pending)
                                                                       .OrderBy(t => t.Timestamp);

            if (_logger.IsInfo)
            {
                _logger.Info($"Deposit: '{deposit.Id}' refund claim pending transactions: {string.Join(", ", pendingTransactions.Select(t => $"{t.Hash} [{t.Type}]"))}");
            }

            if (includedTransaction is null)
            {
                foreach (TransactionInfo transaction in pendingTransactions)
                {
                    Keccak?transactionHash = transaction.Hash;
                    if (transactionHash is null)
                    {
                        if (_logger.IsInfo)
                        {
                            _logger.Info($"Transaction was not found for hash: '{null}' for deposit: '{depositId}' to claim the {claimType}.");
                        }
                        return(false);
                    }

                    transactionDetails = await _blockchainBridge.GetTransactionAsync(transactionHash);

                    if (transactionDetails is null)
                    {
                        if (_logger.IsInfo)
                        {
                            _logger.Info($"Transaction was not found for hash: '{transactionHash}' for deposit: '{depositId}' to claim the {claimType}.");
                        }
                        return(false);
                    }

                    if (transactionDetails.IsPending)
                    {
                        if (_logger.IsInfo)
                        {
                            _logger.Info($"Transaction with hash: '{transactionHash}' for deposit: '{deposit.Id}' {claimType} claim is still pending.");
                        }
                        return(false);
                    }

                    deposit.SetIncludedClaimedRefundTransaction(transactionHash);
                    if (_logger.IsInfo)
                    {
                        _logger.Info($"Transaction with hash: '{transactionHash}', type: '{transaction.Type}' for deposit: '{deposit.Id}' {claimType} claim was included into block: {transactionDetails.BlockNumber}.");
                    }
                    await _depositRepository.UpdateAsync(deposit);

                    includedTransaction = transaction;
                    break;
                }
            }
            else if (includedTransaction.Type == TransactionType.Cancellation)
            {
                return(false);
            }
            else
            {
                transactionDetails = includedTransaction.Hash == null ? null : await _blockchainBridge.GetTransactionAsync(includedTransaction.Hash);

                if (transactionDetails is null)
                {
                    if (_logger.IsWarn)
                    {
                        _logger.Warn($"Transaction (set as included) was not found for hash: '{includedTransaction.Hash}' for deposit: '{deposit.Id}' {claimType} claim.");
                    }
                    return(false);
                }
            }

            if (includedTransaction is null)
            {
                return(false);
            }

            if (_logger.IsInfo)
            {
                _logger.Info($"Trying to claim the {claimType} (transaction hash: '{includedTransaction.Hash}') for deposit: '{depositId}'.");
            }
            TransactionVerifierResult verifierResult = await _transactionVerifier.VerifyAsync(transactionDetails !);

            if (!verifierResult.BlockFound)
            {
                if (_logger.IsWarn)
                {
                    _logger.Warn($"Block number: {transactionDetails!.BlockNumber}, hash: '{transactionDetails.BlockHash}' was not found for transaction hash: '{includedTransaction.Hash}' - {claimType} claim for deposit: '{depositId}' will not confirmed.");
                }
                return(false);
            }

            if (_logger.IsInfo)
            {
                _logger.Info($"The {claimType} claim (transaction hash: '{includedTransaction.Hash}') for deposit: '{depositId}' has {verifierResult.Confirmations} confirmations (required at least {verifierResult.RequiredConfirmations}).");
            }
            if (!verifierResult.Confirmed)
            {
                return(false);
            }

            deposit.SetRefundClaimed();
            await _depositRepository.UpdateAsync(deposit);

            if (_logger.IsInfo)
            {
                _logger.Info($"The {claimType} claim (transaction hash: '{includedTransaction.Hash}') for deposit: '{depositId}' has been confirmed.");
            }

            return(true);
        }