private void processOutputs(TransactionBlockInfo blockInfo, TransfersSubscription sub, ITransactionReader tx, List <TransactionOutputInformationIn> transfers, List <uint> globalIdxs, ref bool contains, ref bool updated)
        {
            TransactionInformation subscribtionTxInfo = new TransactionInformation();

            contains = sub.getContainer().getTransactionInformation(tx.GetTransactionHash(), subscribtionTxInfo);
            updated  = false;

            if (contains)
            {
                if (subscribtionTxInfo.blockHeight == GlobalMembers.WALLET_UNCONFIRMED_TRANSACTION_HEIGHT && blockInfo.height != GlobalMembers.WALLET_UNCONFIRMED_TRANSACTION_HEIGHT)
                {
                    try
                    {
                        // pool->blockchain
                        sub.markTransactionConfirmed(blockInfo, tx.GetTransactionHash(), globalIdxs);
                        updated = true;
                    }
                    catch
                    {
                        m_logger.functorMethod(ERROR, BRIGHT_RED) << "markTransactionConfirmed failed, throw MarkTransactionConfirmedException";
                        throw new MarkTransactionConfirmedException(tx.GetTransactionHash());
                    }
                }
                else
                {
                    Debug.Assert(subscribtionTxInfo.blockHeight == blockInfo.height);
                }
            }
            else
            {
                updated  = sub.addTransaction(blockInfo, tx, transfers);
                contains = updated;
            }
        }
Example #2
0
 public VerboseTransactionInfo(TransactionBlockInfo blockInfo, uint256 id, IEnumerable <VerboseInputInfo> inputs, IEnumerable <VerboseOutputInfo> outputs)
 {
     Id        = id;
     BlockInfo = blockInfo;
     Inputs    = inputs;
     Outputs   = outputs;
 }
        private void processTransaction(TransactionBlockInfo blockInfo, ITransactionReader tx, PreprocessInfo info)
        {
            List <TransactionOutputInformationIn> emptyOutputs          = new List <TransactionOutputInformationIn>();
            List <ITransfersContainer>            transactionContainers = new List <ITransfersContainer>();

            m_logger.functorMethod(TRACE) << "Process transaction, block " << (int)blockInfo.height << ", transaction index " << (int)blockInfo.transactionIndex << ", hash " << tx.GetTransactionHash();
            bool someContainerUpdated = false;

            foreach (var kv in m_subscriptions)
            {
                var it = info.outputs.find(kv.first);
//C++ TO C# CONVERTER TODO TASK: Iterators are only converted within the context of 'while' and 'for' loops:
                auto subscriptionOutputs = (it == info.outputs.end()) ? emptyOutputs : it.second;

                bool containerContainsTx;
                bool containerUpdated;
                processOutputs(blockInfo, *kv.second, tx, subscriptionOutputs, info.globalIdxs, ref containerContainsTx, ref containerUpdated);
                someContainerUpdated = someContainerUpdated || containerUpdated;
                if (containerContainsTx)
                {
                    transactionContainers.emplace_back(kv.second.getContainer());
                }
            }

            if (someContainerUpdated)
            {
                m_logger.functorMethod(TRACE) << "Transaction updated some containers, hash " << tx.GetTransactionHash();
                m_observerManager.notify(IBlockchainConsumerObserver.onTransactionUpdated, this, tx.GetTransactionHash(), transactionContainers);
            }
            else
            {
                m_logger.functorMethod(TRACE) << "Transaction doesn't updated any container, hash " << tx.GetTransactionHash();
            }
        }
        public override std::error_code addUnconfirmedTransaction(ITransactionReader transaction)
        {
            TransactionBlockInfo unconfirmedBlockInfo = new TransactionBlockInfo();

            unconfirmedBlockInfo.height           = GlobalMembers.WALLET_UNCONFIRMED_TRANSACTION_HEIGHT;
            unconfirmedBlockInfo.timestamp        = 0;
            unconfirmedBlockInfo.transactionIndex = 0;

            return(processTransaction(unconfirmedBlockInfo, transaction));
        }
        private std::error_code preprocessOutputs(TransactionBlockInfo blockInfo, ITransactionReader tx, PreprocessInfo info)
        {
            Dictionary <PublicKey, List <uint> > outputs = new Dictionary <PublicKey, List <uint> >();

            try
            {
                GlobalMembers.findMyOutputs(tx, m_viewSecret, m_spendKeys, outputs);
            }
            catch (System.Exception e)
            {
                m_logger.functorMethod(WARNING, BRIGHT_RED) << "Failed to process transaction: " << e.Message << ", transaction hash " << Common.GlobalMembers.podToHex(tx.GetTransactionHash());
                return(std::error_code());
            }

            if (outputs.Count == 0)
            {
                return(std::error_code());
            }

            std::error_code errorCode = new std::error_code();
            var             txHash    = tx.GetTransactionHash();

            if (blockInfo.height != GlobalMembers.WALLET_UNCONFIRMED_TRANSACTION_HEIGHT)
            {
//C++ TO C# CONVERTER TODO TASK: There is no equivalent to 'reinterpret_cast' in C#:
//C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
//ORIGINAL LINE: errorCode = getGlobalIndices(reinterpret_cast<const Hash&>(txHash), info.globalIdxs);
                errorCode.CopyFrom(getGlobalIndices(reinterpret_cast <const Hash&>(txHash), info.globalIdxs));
                if (errorCode != null)
                {
                    return(errorCode);
                }
            }

            foreach (var kv in outputs)
            {
                var it = m_subscriptions.find(kv.first);
//C++ TO C# CONVERTER TODO TASK: Iterators are only converted within the context of 'while' and 'for' loops:
                if (it != m_subscriptions.end())
                {
                    auto transfers = info.outputs[kv.first];
//C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
//ORIGINAL LINE: errorCode = createTransfers(it->second->getKeys(), blockInfo, tx, kv.second, info.globalIdxs, transfers, m_logger);
//C++ TO C# CONVERTER TODO TASK: Iterators are only converted within the context of 'while' and 'for' loops:
                    errorCode.CopyFrom(CryptoNote.GlobalMembers.createTransfers(it.second.getKeys(), blockInfo, tx, kv.second, info.globalIdxs, transfers, m_logger.functorMethod));
                    if (errorCode != null)
                    {
                        return(errorCode);
                    }
                }
            }

            return(std::error_code());
        }
        public bool addTransaction(TransactionBlockInfo blockInfo, ITransactionReader tx, List <TransactionOutputInformationIn> transfersList)
        {
            bool added = transfers.addTransaction(blockInfo, tx, transfersList);

            if (added)
            {
                logger.functorMethod(TRACE) << "Transaction updates balance of wallet " << m_address << ", hash " << tx.GetTransactionHash();
                m_observerManager.notify(ITransfersObserver.onTransactionUpdated, this, tx.GetTransactionHash());
            }

            return(added);
        }
        private std::error_code processTransaction(TransactionBlockInfo blockInfo, ITransactionReader tx)
        {
            PreprocessInfo info = new PreprocessInfo();
            var            ec   = preprocessOutputs(blockInfo, tx, info);

            if (ec != null)
            {
                return(ec);
            }

            processTransaction(blockInfo, tx, info);
            return(std::error_code());
        }
        public override std::error_code onPoolUpdated(List <std::unique_ptr <ITransactionReader> > addedTransactions, List <Hash> deletedTransactions)
        {
            TransactionBlockInfo unconfirmedBlockInfo = new TransactionBlockInfo();

            unconfirmedBlockInfo.timestamp = 0;
            unconfirmedBlockInfo.height    = GlobalMembers.WALLET_UNCONFIRMED_TRANSACTION_HEIGHT;

            std::error_code processingError = new std::error_code();

            foreach (var cryptonoteTransaction in addedTransactions)
            {
                m_poolTxs.emplace(cryptonoteTransaction.getTransactionHash());
//C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
//ORIGINAL LINE: processingError = processTransaction(unconfirmedBlockInfo, *cryptonoteTransaction.get());
                processingError.CopyFrom(processTransaction(unconfirmedBlockInfo, *cryptonoteTransaction.get()));
                if (processingError != null)
                {
                    foreach (var sub in m_subscriptions)
                    {
                        sub.second.onError(processingError, GlobalMembers.WALLET_UNCONFIRMED_TRANSACTION_HEIGHT);
                    }

                    return(processingError);
                }
            }

            foreach (var deletedTxHash in deletedTransactions)
            {
                m_poolTxs.erase(deletedTxHash);

                m_observerManager.notify(IBlockchainConsumerObserver.onTransactionDeleteBegin, this, deletedTxHash);
                foreach (var sub in m_subscriptions)
                {
//C++ TO C# CONVERTER TODO TASK: There is no equivalent to 'reinterpret_cast' in C#:
                    sub.second.deleteUnconfirmedTransaction(*reinterpret_cast <const Hash>(deletedTxHash));
                }

                m_observerManager.notify(IBlockchainConsumerObserver.onTransactionDeleteEnd, this, deletedTxHash);
            }

            return(std::error_code());
        }
        public override uint onNewBlocks(CompleteBlock[] blocks, uint startHeight, uint count)
        {
            Debug.Assert(blocks);
            Debug.Assert(count > 0);

//C++ TO C# CONVERTER TODO TASK: C# does not allow declaring types within methods:
//	struct Tx
//	{
//	  TransactionBlockInfo blockInfo;
//	  const ITransactionReader* tx;
//	  bool isLastTransactionInBlock;
//	};

//C++ TO C# CONVERTER TODO TASK: C# does not allow declaring types within methods:
//	struct PreprocessedTx : Tx, PreprocessInfo
//	{
//	};

            List <PreprocessedTx> preprocessedTransactions = new List <PreprocessedTx>();
            object preprocessedTransactionsMutex           = new object();

            uint workers = std::thread.hardware_concurrency();

            if (workers == 0)
            {
                workers = 2;
            }

            BlockingQueue <Tx> inputQueue = new BlockingQueue <Tx>(workers * 2);

            std::atomic <bool> stopProcessing  = new std::atomic <bool>(false);
            std::atomic <uint> emptyBlockCount = new std::atomic <uint>(0);

//C++ TO C# CONVERTER TODO TASK: Lambda expressions cannot be assigned to 'var':
            var pushingThread = std::async(std::launch.async, () =>
            {
                for (uint i = 0; i < count && stopProcessing == null; ++i)
                {
                    auto block = blocks[i].block;

                    if (!block.is_initialized())
                    {
                        ++emptyBlockCount;
                        continue;
                    }

                    // filter by syncStartTimestamp
                    if (m_syncStart.timestamp != 0 && block.timestamp < m_syncStart.timestamp)
                    {
                        ++emptyBlockCount;
                        continue;
                    }

                    TransactionBlockInfo blockInfo = new TransactionBlockInfo();
                    blockInfo.height           = startHeight + i;
                    blockInfo.timestamp        = block.timestamp;
                    blockInfo.transactionIndex = 0; // position in block

                    foreach (var tx in blocks[i].transactions)
                    {
                        var pubKey = tx.GetTransactionPublicKey();
                        if (pubKey == NULL_PUBLIC_KEY)
                        {
                            ++blockInfo.transactionIndex;
                            continue;
                        }

                        bool isLastTransactionInBlock = blockInfo.transactionIndex + 1 == blocks[i].transactions.size();
                        Tx item = new Tx(blockInfo, tx.get(), isLastTransactionInBlock);
                        inputQueue.push(new Tx(item));
                        ++blockInfo.transactionIndex;
                    }
                }

                inputQueue.close();
            });

//C++ TO C# CONVERTER TODO TASK: Lambda expressions cannot be assigned to 'var':
            var processingFunction = () =>
            {
                Tx item            = new Tx();
                std::error_code ec = new std::error_code();
                while (stopProcessing == null && inputQueue.pop(ref item))
                {
                    PreprocessedTx output = new PreprocessedTx();
                    (Tx)output = item;

                    ec.CopyFrom(preprocessOutputs(item.blockInfo, *item.tx, output));
                    if (ec != null)
                    {
                        stopProcessing = true;
                        break;
                    }

                    lock (preprocessedTransactionsMutex)
                    {
                        preprocessedTransactions.Add(std::move(output));
                    }
                }
                return(ec);
            };

            List <std::future <std::error_code> > processingThreads = new List <std::future <std::error_code> >();

            for (uint i = 0; i < workers; ++i)
            {
                processingThreads.Add(std::async(std::launch.async, processingFunction));
            }

            std::error_code processingError = new std::error_code();

            foreach (var f in processingThreads)
            {
                try
                {
                    std::error_code ec = f.get();
                    if (processingError == null && ec != null)
                    {
//C++ TO C# CONVERTER TODO TASK: The following line was determined to be a copy assignment (rather than a reference assignment) - this should be verified and a 'CopyFrom' method should be created:
//ORIGINAL LINE: processingError = ec;
                        processingError.CopyFrom(ec);
                    }
                }
                catch (std::system_error e)
                {
                    processingError = e.code();
                }
                catch (System.Exception)
                {
                    processingError = std::make_error_code(std::errc.operation_canceled);
                }
            }

            if (processingError != null)
            {
                forEachSubscription((TransfersSubscription sub) =>
                {
                    sub.onError(processingError, startHeight);
                });

                return(0);
            }

//C++ TO C# CONVERTER TODO TASK: The following line was determined to contain a copy constructor call - this should be verified and a copy constructor should be created:
//ORIGINAL LINE: ClassicVector<Crypto::Hash> blockHashes = getBlockHashes(blocks, count);
            List <Crypto.Hash> blockHashes = GlobalMembers.getBlockHashes(new CryptoNote.CompleteBlock(blocks), count);

            m_observerManager.notify(IBlockchainConsumerObserver.onBlocksAdded, this, blockHashes);

            // sort by block height and transaction index in block
//C++ TO C# CONVERTER TODO TASK: The 'Compare' parameter of std::sort produces a boolean value, while the .NET Comparison parameter produces a tri-state result:
//ORIGINAL LINE: std::sort(preprocessedTransactions.begin(), preprocessedTransactions.end(), [](const PreprocessedTx& a, const PreprocessedTx& b)
            preprocessedTransactions.Sort((PreprocessedTx a, PreprocessedTx b) =>
            {
                return(std::tie(a.blockInfo.height, a.blockInfo.transactionIndex) < std::tie(b.blockInfo.height, b.blockInfo.transactionIndex));
            });

            uint processedBlockCount = (uint)emptyBlockCount;

            try
            {
                foreach (var tx in preprocessedTransactions)
                {
                    processTransaction(tx.blockInfo, *tx.tx, tx);

                    if (tx.isLastTransactionInBlock)
                    {
                        ++processedBlockCount;
                        m_logger.functorMethod(TRACE) << "Processed block " << (int)processedBlockCount << " of " << (int)count << ", last processed block index " << tx.blockInfo.height << ", hash " << blocks[processedBlockCount - 1].blockHash;

                        var newHeight = startHeight + processedBlockCount - 1;
//C++ TO C# CONVERTER TODO TASK: Only lambda expressions having all locals passed by reference can be converted to C#:
//ORIGINAL LINE: forEachSubscription([newHeight](TransfersSubscription& sub)
                        forEachSubscription((TransfersSubscription sub) =>
                        {
                            sub.advanceHeight(newHeight);
                        });
                    }
                }
            }
            catch (MarkTransactionConfirmedException e)
            {
                m_logger.functorMethod(ERROR, BRIGHT_RED) << "Failed to process block transactions: failed to confirm transaction " << e.getTxHash() << ", remove this transaction from all containers and transaction pool";
                forEachSubscription((TransfersSubscription sub) =>
                {
                    sub.deleteUnconfirmedTransaction(e.getTxHash());
                });

                m_poolTxs.erase(e.getTxHash());
            }
            catch (System.Exception e)
            {
                m_logger.functorMethod(ERROR, BRIGHT_RED) << "Failed to process block transactions, exception: " << e.Message;
            }
            catch
            {
                m_logger.functorMethod(ERROR, BRIGHT_RED) << "Failed to process block transactions, unknown exception";
            }

            if (processedBlockCount < count)
            {
                uint detachIndex = startHeight + processedBlockCount;
                m_logger.functorMethod(ERROR, BRIGHT_RED) << "Not all block transactions are processed, fully processed block count: " << (int)processedBlockCount << " of " << (int)count << ", last processed block hash " << (processedBlockCount > 0 ? blocks[processedBlockCount - 1].blockHash : GlobalMembers.NULL_HASH) << ", detach block index " << (int)detachIndex << " to remove partially processed block";
//C++ TO C# CONVERTER TODO TASK: Only lambda expressions having all locals passed by reference can be converted to C#:
//ORIGINAL LINE: forEachSubscription([detachIndex](TransfersSubscription& sub)
                forEachSubscription((TransfersSubscription sub) =>
                {
                    sub.onBlockchainDetach(detachIndex);
                });
            }

            return(processedBlockCount);
        }
 public void markTransactionConfirmed(TransactionBlockInfo block, Hash transactionHash, List <uint> globalIndices)
 {
     transfers.markTransactionConfirmed(block, transactionHash, globalIndices);
     m_observerManager.notify(ITransfersObserver.onTransactionUpdated, this, transactionHash);
 }