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); }