// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // // Bytecoin is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Bytecoin is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . #include "Core.h" #include #include #include "../CryptoNoteConfig.h" #include "../Common/CommandLine.h" #include "../Common/Util.h" #include "../Common/StringTools.h" #include "../crypto/crypto.h" #include "../CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h" #include "../Logging/LoggerRef.h" #include "../Rpc/CoreRpcServerCommandsDefinitions.h" #include "CryptoNoteFormatUtils.h" #include "CryptoNoteTools.h" #include "CryptoNoteStatInfo.h" #include "Miner.h" #include "TransactionExtra.h" #include "IBlock.h" #undef ERROR using namespace Logging; #include "CryptoNoteCore/CoreConfig.h" using namespace Common; namespace CryptoNote { class BlockWithTransactions : public IBlock { public: virtual const Block& getBlock() const override { return block; } virtual size_t getTransactionCount() const override { return transactions.size(); } virtual const Transaction& getTransaction(size_t index) const override { assert(index < transactions.size()); return transactions[index]; } private: Block block; std::vector transactions; friend class core; }; core::core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger) : m_currency(currency), logger(logger, "core"), m_mempool(currency, m_blockchain, m_timeProvider, logger), m_blockchain(currency, m_mempool, logger), m_miner(new miner(currency, *this, logger)), m_starter_message_showed(false) { set_cryptonote_protocol(pprotocol); m_blockchain.addObserver(this); m_mempool.addObserver(this); } //----------------------------------------------------------------------------------------------- core::~core() { m_blockchain.removeObserver(this); } void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol) { if (pprotocol) m_pprotocol = pprotocol; else m_pprotocol = &m_protocol_stub; } //----------------------------------------------------------------------------------- void core::set_checkpoints(Checkpoints&& chk_pts) { m_blockchain.setCheckpoints(std::move(chk_pts)); } //----------------------------------------------------------------------------------- void core::init_options(boost::program_options::options_description& /*desc*/) { } bool core::handle_command_line(const boost::program_options::variables_map& vm) { m_config_folder = command_line::get_arg(vm, command_line::arg_data_dir); return true; } bool core::is_ready() { return !m_blockchain.isStoringBlockchain(); } uint32_t core::get_current_blockchain_height() { return m_blockchain.getCurrentBlockchainHeight(); } void core::get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) { assert(m_blockchain.getCurrentBlockchainHeight() > 0); top_id = m_blockchain.getTailId(height); } bool core::get_blocks(uint32_t start_offset, uint32_t count, std::list& blocks, std::list& txs) { return m_blockchain.getBlocks(start_offset, count, blocks, txs); } bool core::get_blocks(uint32_t start_offset, uint32_t count, std::list& blocks) { return m_blockchain.getBlocks(start_offset, count, blocks); } void core::getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool) { m_blockchain.getTransactions(txs_ids, txs, missed_txs, checkTxPool); } bool core::get_alternative_blocks(std::list& blocks) { return m_blockchain.getAlternativeBlocks(blocks); } size_t core::get_alternative_blocks_count() { return m_blockchain.getAlternativeBlocksCount(); } //----------------------------------------------------------------------------------------------- bool core::init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing) { m_config_folder = config.configFolder; bool r = m_mempool.init(m_config_folder); if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize memory pool"; return false; } r = m_blockchain.init(m_config_folder, load_existing); if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize blockchain storage"; return false; } r = m_miner->init(minerConfig); if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize blockchain storage"; return false; } return load_state_data(); } bool core::set_genesis_block(const Block& b) { return m_blockchain.resetAndSetGenesisBlock(b); } bool core::load_state_data() { // may be some code later return true; } bool core::deinit() { m_miner->stop(); m_mempool.deinit(); m_blockchain.deinit(); return true; } size_t core::addChain(const std::vector& chain) { size_t blocksCounter = 0; for (const IBlock* block : chain) { bool allTransactionsAdded = true; for (size_t txNumber = 0; txNumber < block->getTransactionCount(); ++txNumber) { const Transaction& tx = block->getTransaction(txNumber); Crypto::Hash txHash = NULL_HASH; size_t blobSize = 0; getObjectHash(tx, txHash, blobSize); tx_verification_context tvc = boost::value_initialized(); if (!handleIncomingTransaction(tx, txHash, blobSize, tvc, true)) { logger(ERROR, BRIGHT_RED) << "core::addChain() failed to handle transaction " << txHash << " from block " << blocksCounter << "/" << chain.size(); allTransactionsAdded = false; break; } } if (!allTransactionsAdded) { break; } block_verification_context bvc = boost::value_initialized(); m_blockchain.addNewBlock(block->getBlock(), bvc); if (bvc.m_marked_as_orphaned || bvc.m_verifivation_failed) { logger(ERROR, BRIGHT_RED) << "core::addChain() failed to handle incoming block " << get_block_hash(block->getBlock()) << ", " << blocksCounter << "/" << chain.size(); break; } ++blocksCounter; // TODO m_dispatcher.yield()? } return blocksCounter; } bool core::handle_incoming_tx(const BinaryArray& tx_blob, tx_verification_context& tvc, bool keeped_by_block) { //Deprecated. Should be removed with CryptoNoteProtocolHandler. tvc = boost::value_initialized(); //want to process all transactions sequentially if (tx_blob.size() > m_currency.maxTxSize()) { logger(INFO) << "WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected"; tvc.m_verifivation_failed = true; return false; } Crypto::Hash tx_hash = NULL_HASH; Crypto::Hash tx_prefixt_hash = NULL_HASH; Transaction tx; if (!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) { logger(INFO) << "WRONG TRANSACTION BLOB, Failed to parse, rejected"; tvc.m_verifivation_failed = true; return false; } //std::cout << "!"<< tx.inputs.size() << std::endl; return handleIncomingTransaction(tx, tx_hash, tx_blob.size(), tvc, keeped_by_block); } bool core::get_stat_info(core_stat_info& st_inf) { st_inf.mining_speed = m_miner->get_speed(); st_inf.alternative_blocks = m_blockchain.getAlternativeBlocksCount(); st_inf.blockchain_height = m_blockchain.getCurrentBlockchainHeight(); st_inf.tx_pool_size = m_mempool.get_transactions_count(); st_inf.top_block_id_str = Common::podToHex(m_blockchain.getTailId()); return true; } bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) { if (!tx.inputs.size()) { logger(ERROR) << "tx with empty inputs, rejected for tx id= " << getObjectHash(tx); return false; } if (!check_inputs_types_supported(tx)) { logger(ERROR) << "unsupported input types for tx id= " << getObjectHash(tx); return false; } std::string errmsg; if (!check_outs_valid(tx, &errmsg)) { logger(ERROR) << "tx with invalid outputs, rejected for tx id= " << getObjectHash(tx) << ": " << errmsg; return false; } if (!check_money_overflow(tx)) { logger(ERROR) << "tx have money overflow, rejected for tx id= " << getObjectHash(tx); return false; } uint64_t amount_in = 0; get_inputs_money_amount(tx, amount_in); uint64_t amount_out = get_outs_money_amount(tx); if (amount_in <= amount_out) { logger(ERROR) << "tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << getObjectHash(tx); return false; } //check if tx use different key images if (!check_tx_inputs_keyimages_diff(tx)) { logger(ERROR) << "tx has a few inputs with identical keyimages"; return false; } if (!checkMultisignatureInputsDiff(tx)) { logger(ERROR) << "tx has a few multisignature inputs with identical output indexes"; return false; } return true; } bool core::check_tx_inputs_keyimages_diff(const Transaction& tx) { std::unordered_set ki; for (const auto& in : tx.inputs) { if (in.type() == typeid(KeyInput)) { if (!ki.insert(boost::get(in).keyImage).second) return false; } } return true; } size_t core::get_blockchain_total_transactions() { return m_blockchain.getTotalTransactions(); } //bool core::get_outs(uint64_t amount, std::list& pkeys) //{ // return m_blockchain.get_outs(amount, pkeys); //} bool core::add_new_tx(const Transaction& tx, const Crypto::Hash& tx_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block) { //Locking on m_mempool and m_blockchain closes possibility to add tx to memory pool which is already in blockchain std::lock_guard lk(m_mempool); LockedBlockchainStorage lbs(m_blockchain); if (m_blockchain.haveTransaction(tx_hash)) { logger(TRACE) << "tx " << tx_hash << " is already in blockchain"; return true; } if (m_mempool.have_tx(tx_hash)) { logger(TRACE) << "tx " << tx_hash << " is already in transaction pool"; return true; } return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block); } bool core::get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const BinaryArray& ex_nonce) { size_t median_size; uint64_t already_generated_coins; { LockedBlockchainStorage blockchainLock(m_blockchain); height = m_blockchain.getCurrentBlockchainHeight(); diffic = m_blockchain.getDifficultyForNextBlock(); if (!(diffic)) { logger(ERROR, BRIGHT_RED) << "difficulty overhead."; return false; } b = boost::value_initialized(); b.majorVersion = m_blockchain.getBlockMajorVersionForHeight(height); if (BLOCK_MAJOR_VERSION_1 == b.majorVersion) { b.minorVersion = BLOCK_MINOR_VERSION_1; } else if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) { b.minorVersion = BLOCK_MINOR_VERSION_0; b.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; b.parentBlock.majorVersion = BLOCK_MINOR_VERSION_0; b.parentBlock.transactionCount = 1; TransactionExtraMergeMiningTag mm_tag = boost::value_initialized(); if (!appendMergeMiningTagToExtra(b.parentBlock.baseTransaction.extra, mm_tag)) { logger(ERROR, BRIGHT_RED) << "Failed to append merge mining tag to extra of the parent block miner transaction"; return false; } } b.previousBlockHash = get_tail_id(); b.timestamp = time(NULL); median_size = m_blockchain.getCurrentCumulativeBlocksizeLimit() / 2; already_generated_coins = m_blockchain.getCoinsInCirculation(); } size_t txs_size; uint64_t fee; if (!m_mempool.fill_block_template(b, median_size, m_currency.maxBlockCumulativeSize(height), already_generated_coins, txs_size, fee)) { return false; } /* two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size */ //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size bool penalizeFee = b.majorVersion > BLOCK_MAJOR_VERSION_1; bool r = m_currency.constructMinerTx(height, median_size, already_generated_coins, txs_size, fee, adr, b.baseTransaction, ex_nonce, 11, penalizeFee); if (!r) { logger(ERROR, BRIGHT_RED) << "Failed to construct miner tx, first chance"; return false; } size_t cumulative_size = txs_size + getObjectBinarySize(b.baseTransaction); for (size_t try_count = 0; try_count != 10; ++try_count) { r = m_currency.constructMinerTx(height, median_size, already_generated_coins, cumulative_size, fee, adr, b.baseTransaction, ex_nonce, 11, penalizeFee); if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to construct miner tx, second chance"; return false; } size_t coinbase_blob_size = getObjectBinarySize(b.baseTransaction); if (coinbase_blob_size > cumulative_size - txs_size) { cumulative_size = txs_size + coinbase_blob_size; continue; } if (coinbase_blob_size < cumulative_size - txs_size) { size_t delta = cumulative_size - txs_size - coinbase_blob_size; b.baseTransaction.extra.insert(b.baseTransaction.extra.end(), delta, 0); //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len. if (cumulative_size != txs_size + getObjectBinarySize(b.baseTransaction)) { if (!(cumulative_size + 1 == txs_size + getObjectBinarySize(b.baseTransaction))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.baseTransaction)=" << getObjectBinarySize(b.baseTransaction); return false; } b.baseTransaction.extra.resize(b.baseTransaction.extra.size() - 1); if (cumulative_size != txs_size + getObjectBinarySize(b.baseTransaction)) { //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size logger(TRACE, BRIGHT_RED) << "Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1; cumulative_size += delta - 1; continue; } logger(DEBUGGING, BRIGHT_GREEN) << "Setting extra for block: " << b.baseTransaction.extra.size() << ", try_count=" << try_count; } } if (!(cumulative_size == txs_size + getObjectBinarySize(b.baseTransaction))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.baseTransaction)=" << getObjectBinarySize(b.baseTransaction); return false; } return true; } logger(ERROR, BRIGHT_RED) << "Failed to create_block_template with " << 10 << " tries"; return false; } std::vector core::findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, uint32_t& totalBlockCount, uint32_t& startBlockIndex) { assert(!remoteBlockIds.empty()); assert(remoteBlockIds.back() == m_blockchain.getBlockIdByHeight(0)); return m_blockchain.findBlockchainSupplement(remoteBlockIds, maxCount, totalBlockCount, startBlockIndex); } void core::print_blockchain(uint32_t start_index, uint32_t end_index) { m_blockchain.print_blockchain(start_index, end_index); } void core::print_blockchain_index() { m_blockchain.print_blockchain_index(); } void core::print_blockchain_outs(const std::string& file) { m_blockchain.print_blockchain_outs(file); } bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) { return m_blockchain.getRandomOutsByAmount(req, res); } bool core::get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector& indexs) { return m_blockchain.getTransactionOutputGlobalIndexes(tx_id, indexs); } bool core::getOutByMSigGIndex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out) { return m_blockchain.get_out_by_msig_gindex(amount, gindex, out); } void core::pause_mining() { m_miner->pause(); } void core::update_block_template_and_resume_mining() { update_miner_block_template(); m_miner->resume(); } bool core::handle_block_found(Block& b) { block_verification_context bvc = boost::value_initialized(); handle_incoming_block(b, bvc, true, true); if (bvc.m_verifivation_failed) { logger(ERROR) << "mined block failed verification"; } return bvc.m_added_to_main_chain; } void core::on_synchronized() { m_miner->on_synchronized(); } bool core::getPoolChanges(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, std::vector& addedTxs, std::vector& deletedTxsIds) { if (tailBlockId != m_blockchain.getTailId()) { return false; } getPoolChanges(knownTxsIds, addedTxs, deletedTxsIds); return true; } bool core::getPoolChangesLite(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, std::vector& addedTxs, std::vector& deletedTxsIds) { std::vector added; if (!getPoolChanges(tailBlockId, knownTxsIds, added, deletedTxsIds)) { return false; } for (const auto& tx: added) { TransactionPrefixInfo tpi; tpi.txPrefix = tx; tpi.txHash = getObjectHash(tx); addedTxs.push_back(std::move(tpi)); } return true; } void core::getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, std::vector& deletedTxsIds) { std::vector addedTxsIds; m_mempool.get_difference(knownTxsIds, addedTxsIds, deletedTxsIds); std::vector misses; m_mempool.getTransactions(addedTxsIds, addedTxs, misses); assert(misses.empty()); } bool core::handle_incoming_block_blob(const BinaryArray& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) { if (block_blob.size() > m_currency.maxBlockBlobSize()) { logger(INFO) << "WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"; bvc.m_verifivation_failed = true; return false; } Block b; if (!fromBinaryArray(b, block_blob)) { logger(INFO) << "Failed to parse and validate new block"; bvc.m_verifivation_failed = true; return false; } return handle_incoming_block(b, bvc, control_miner, relay_block); } bool core::handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block) { if (control_miner) { pause_mining(); } m_blockchain.addNewBlock(b, bvc); if (control_miner) { update_block_template_and_resume_mining(); } if (relay_block && bvc.m_added_to_main_chain) { std::list missed_txs; std::list txs; m_blockchain.getTransactions(b.transactionHashes, txs, missed_txs); if (!missed_txs.empty() && getBlockIdByHeight(get_block_height(b)) != get_block_hash(b)) { logger(INFO) << "Block added, but it seems that reorganize just happened after that, do not relay this block"; } else { if (!(txs.size() == b.transactionHashes.size() && missed_txs.empty())) { logger(ERROR, BRIGHT_RED) << "can't find some transactions in found block:" << get_block_hash(b) << " txs.size()=" << txs.size() << ", b.transactionHashes.size()=" << b.transactionHashes.size() << ", missed_txs.size()" << missed_txs.size(); return false; } NOTIFY_NEW_BLOCK::request arg; arg.hop = 0; arg.current_blockchain_height = m_blockchain.getCurrentBlockchainHeight(); BinaryArray blockBa; bool r = toBinaryArray(b, blockBa); if (!(r)) { logger(ERROR, BRIGHT_RED) << "failed to serialize block"; return false; } arg.b.block = asString(blockBa); for (auto& tx : txs) { arg.b.txs.push_back(asString(toBinaryArray(tx))); } m_pprotocol->relay_block(arg); } } return true; } Crypto::Hash core::get_tail_id() { return m_blockchain.getTailId(); } size_t core::get_pool_transactions_count() { return m_mempool.get_transactions_count(); } bool core::have_block(const Crypto::Hash& id) { return m_blockchain.haveBlock(id); } bool core::parse_tx_from_blob(Transaction& tx, Crypto::Hash& tx_hash, Crypto::Hash& tx_prefix_hash, const BinaryArray& blob) { return parseAndValidateTransactionFromBinaryArray(blob, tx, tx_hash, tx_prefix_hash); } bool core::check_tx_syntax(const Transaction& tx) { return true; } std::vector core::getPoolTransactions() { std::list txs; m_mempool.get_transactions(txs); std::vector result; for (auto& tx : txs) { result.emplace_back(std::move(tx)); } return result; } std::vector core::buildSparseChain() { assert(m_blockchain.getCurrentBlockchainHeight() != 0); return m_blockchain.buildSparseChain(); } std::vector core::buildSparseChain(const Crypto::Hash& startBlockId) { LockedBlockchainStorage lbs(m_blockchain); assert(m_blockchain.haveBlock(startBlockId)); return m_blockchain.buildSparseChain(startBlockId); } bool core::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { //Deprecated. Should be removed with CryptoNoteProtocolHandler. return m_blockchain.handleGetObjects(arg, rsp); } Crypto::Hash core::getBlockIdByHeight(uint32_t height) { LockedBlockchainStorage lbs(m_blockchain); if (height < m_blockchain.getCurrentBlockchainHeight()) { return m_blockchain.getBlockIdByHeight(height); } else { return NULL_HASH; } } bool core::getBlockByHash(const Crypto::Hash &h, Block &blk) { return m_blockchain.getBlockByHash(h, blk); } bool core::getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight) { return m_blockchain.getBlockHeight(blockId, blockHeight); } //void core::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { // m_blockchain.get_all_known_block_ids(main, alt, invalid); //} std::string core::print_pool(bool short_format) { return m_mempool.print_pool(short_format); } bool core::update_miner_block_template() { m_miner->on_block_chain_update(); return true; } bool core::on_idle() { if (!m_starter_message_showed) { logger(INFO) << ENDL << "**********************************************************************" << ENDL << "The daemon will start synchronizing with the network. It may take up to several hours." << ENDL << ENDL << "You can set the level of process detailization* through \"set_log \" command*, where is between 0 (no details) and 4 (very verbose)." << ENDL << ENDL << "Use \"help\" command to see the list of available commands." << ENDL << ENDL << "Note: in case you need to interrupt the process, use \"exit\" command. Otherwise, the current progress won't be saved." << ENDL << "**********************************************************************"; m_starter_message_showed = true; } m_miner->on_idle(); m_mempool.on_idle(); return true; } bool core::addObserver(ICoreObserver* observer) { return m_observerManager.add(observer); } bool core::removeObserver(ICoreObserver* observer) { return m_observerManager.remove(observer); } void core::blockchainUpdated() { m_observerManager.notify(&ICoreObserver::blockchainUpdated); } void core::txDeletedFromPool() { poolUpdated(); } void core::poolUpdated() { m_observerManager.notify(&ICoreObserver::poolUpdated); } bool core::queryBlocks(const std::vector& knownBlockIds, uint64_t timestamp, uint32_t& resStartHeight, uint32_t& resCurrentHeight, uint32_t& resFullOffset, std::vector& entries) { LockedBlockchainStorage lbs(m_blockchain); uint32_t currentHeight = lbs->getCurrentBlockchainHeight(); uint32_t startOffset = 0; uint32_t startFullOffset = 0; if (!findStartAndFullOffsets(knownBlockIds, timestamp, startOffset, startFullOffset)) { return false; } resFullOffset = startFullOffset; std::vector blockIds = findIdsForShortBlocks(startOffset, startFullOffset); entries.reserve(blockIds.size()); for (const auto& id : blockIds) { entries.push_back(BlockFullInfo()); entries.back().block_id = id; } resCurrentHeight = currentHeight; resStartHeight = startOffset; uint32_t blocksLeft = static_cast(std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - entries.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT))); if (blocksLeft == 0) { return true; } std::list blocks; lbs->getBlocks(startFullOffset, blocksLeft, blocks); for (auto& b : blocks) { BlockFullInfo item; item.block_id = get_block_hash(b); if (b.timestamp >= timestamp) { // query transactions std::list txs; std::list missedTxs; lbs->getTransactions(b.transactionHashes, txs, missedTxs); // fill data block_complete_entry& completeEntry = item; completeEntry.block = asString(toBinaryArray(b)); for (auto& tx : txs) { completeEntry.txs.push_back(asString(toBinaryArray(tx))); } } entries.push_back(std::move(item)); } return true; } bool core::findStartAndFullOffsets(const std::vector& knownBlockIds, uint64_t timestamp, uint32_t& startOffset, uint32_t& startFullOffset) { LockedBlockchainStorage lbs(m_blockchain); if (knownBlockIds.empty()) { logger(ERROR, BRIGHT_RED) << "knownBlockIds is empty"; return false; } if (knownBlockIds.back() != m_blockchain.getBlockIdByHeight(0)) { logger(ERROR, BRIGHT_RED) << "knownBlockIds doesn't end with genesis block hash: " << knownBlockIds.back(); return false; } startOffset = lbs->findBlockchainSupplement(knownBlockIds); if (!lbs->getLowerBound(timestamp, startOffset, startFullOffset)) { startFullOffset = startOffset; } return true; } std::vector core::findIdsForShortBlocks(uint32_t startOffset, uint32_t startFullOffset) { assert(startOffset <= startFullOffset); LockedBlockchainStorage lbs(m_blockchain); std::vector result; if (startOffset < startFullOffset) { result = lbs->getBlockIds(startOffset, std::min(static_cast(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT), startFullOffset - startOffset)); } return result; } bool core::queryBlocksLite(const std::vector& knownBlockIds, uint64_t timestamp, uint32_t& resStartHeight, uint32_t& resCurrentHeight, uint32_t& resFullOffset, std::vector& entries) { LockedBlockchainStorage lbs(m_blockchain); resCurrentHeight = lbs->getCurrentBlockchainHeight(); resStartHeight = 0; resFullOffset = 0; if (!findStartAndFullOffsets(knownBlockIds, timestamp, resStartHeight, resFullOffset)) { return false; } std::vector blockIds = findIdsForShortBlocks(resStartHeight, resFullOffset); entries.reserve(blockIds.size()); for (const auto& id : blockIds) { entries.push_back(BlockShortInfo()); entries.back().blockId = id; } uint32_t blocksLeft = static_cast(std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - entries.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT))); if (blocksLeft == 0) { return true; } std::list blocks; lbs->getBlocks(resFullOffset, blocksLeft, blocks); for (auto& b : blocks) { BlockShortInfo item; item.blockId = get_block_hash(b); if (b.timestamp >= timestamp) { std::list txs; std::list missedTxs; lbs->getTransactions(b.transactionHashes, txs, missedTxs); item.block = asString(toBinaryArray(b)); for (const auto& tx: txs) { TransactionPrefixInfo info; info.txPrefix = tx; info.txHash = getObjectHash(tx); item.txPrefixes.push_back(std::move(info)); } } entries.push_back(std::move(item)); } return true; } bool core::getBackwardBlocksSizes(uint32_t fromHeight, std::vector& sizes, size_t count) { return m_blockchain.getBackwardBlocksSize(fromHeight, sizes, count); } bool core::getBlockSize(const Crypto::Hash& hash, size_t& size) { return m_blockchain.getBlockSize(hash, size); } bool core::getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins) { return m_blockchain.getAlreadyGeneratedCoins(hash, generatedCoins); } bool core::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, bool penalizeFee, uint64_t& reward, int64_t& emissionChange) { return m_currency.getBlockReward(medianSize, currentBlockSize, alreadyGeneratedCoins, fee, penalizeFee, reward, emissionChange); } bool core::scanOutputkeysForIndices(const KeyInput& txInToKey, std::list>& outputReferences) { struct outputs_visitor { std::list>& m_resultsCollector; outputs_visitor(std::list>& resultsCollector):m_resultsCollector(resultsCollector){} bool handle_output(const Transaction& tx, const TransactionOutput& out, size_t transactionOutputIndex) { m_resultsCollector.push_back(std::make_pair(getObjectHash(tx), transactionOutputIndex)); return true; } }; outputs_visitor vi(outputReferences); return m_blockchain.scanOutputKeysForIndexes(txInToKey, vi); } bool core::getBlockDifficulty(uint32_t height, difficulty_type& difficulty) { difficulty = m_blockchain.blockDifficulty(height); return true; } bool core::getBlockContainingTx(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight) { return m_blockchain.getBlockContainingTransaction(txId, blockId, blockHeight); } bool core::getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair& outputReference) { return m_blockchain.getMultisigOutputReference(txInMultisig, outputReference); } bool core::getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions) { return m_blockchain.getGeneratedTransactionsNumber(height, generatedTransactions); } bool core::getOrphanBlocksByHeight(uint32_t height, std::vector& blocks) { std::vector blockHashes; if (!m_blockchain.getOrphanBlockIdsByHeight(height, blockHashes)) { return false; } for (const Crypto::Hash& hash : blockHashes) { Block blk; if (!getBlockByHash(hash, blk)) { return false; } blocks.push_back(std::move(blk)); } return true; } bool core::getBlocksByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) { std::vector blockHashes; if (!m_blockchain.getBlockIdsByTimestamp(timestampBegin, timestampEnd, blocksNumberLimit, blockHashes, blocksNumberWithinTimestamps)) { return false; } for (const Crypto::Hash& hash : blockHashes) { Block blk; if (!getBlockByHash(hash, blk)) { return false; } blocks.push_back(std::move(blk)); } return true; } bool core::getPoolTransactionsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) { std::vector poolTransactionHashes; if (!m_mempool.getTransactionIdsByTimestamp(timestampBegin, timestampEnd, transactionsNumberLimit, poolTransactionHashes, transactionsNumberWithinTimestamps)) { return false; } std::list txs; std::list missed_txs; getTransactions(poolTransactionHashes, txs, missed_txs, true); if (missed_txs.size() > 0) { return false; } transactions.insert(transactions.end(), txs.begin(), txs.end()); return true; } bool core::getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) { std::vector blockchainTransactionHashes; if (!m_blockchain.getTransactionIdsByPaymentId(paymentId, blockchainTransactionHashes)) { return false; } std::vector poolTransactionHashes; if (!m_mempool.getTransactionIdsByPaymentId(paymentId, poolTransactionHashes)) { return false; } std::list txs; std::list missed_txs; blockchainTransactionHashes.insert(blockchainTransactionHashes.end(), poolTransactionHashes.begin(), poolTransactionHashes.end()); getTransactions(blockchainTransactionHashes, txs, missed_txs, true); if (missed_txs.size() > 0) { return false; } transactions.insert(transactions.end(), txs.begin(), txs.end()); return true; } std::error_code core::executeLocked(const std::function& func) { std::lock_guard lk(m_mempool); LockedBlockchainStorage lbs(m_blockchain); return func(); } uint64_t core::getNextBlockDifficulty() { return m_blockchain.getDifficultyForNextBlock(); } uint64_t core::getTotalGeneratedAmount() { return m_blockchain.getCoinsInCirculation(); } bool core::handleIncomingTransaction(const Transaction& tx, const Crypto::Hash& txHash, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) { if (!check_tx_syntax(tx)) { logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << txHash << " syntax, rejected"; tvc.m_verifivation_failed = true; return false; } if (!check_tx_semantic(tx, keptByBlock)) { logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << txHash << " semantic, rejected"; tvc.m_verifivation_failed = true; return false; } bool r = add_new_tx(tx, txHash, blobSize, tvc, keptByBlock); if (tvc.m_verifivation_failed) { if (!tvc.m_tx_fee_too_small) { logger(ERROR) << "Transaction verification failed: " << txHash; } else { logger(INFO) << "Transaction verification failed: " << txHash; } } else if (tvc.m_verifivation_impossible) { logger(ERROR) << "Transaction verification impossible: " << txHash; } if (tvc.m_added_to_pool) { logger(DEBUGGING) << "tx added: " << txHash; poolUpdated(); } return r; } std::unique_ptr core::getBlock(const Crypto::Hash& blockId) { std::lock_guard lk(m_mempool); LockedBlockchainStorage lbs(m_blockchain); std::unique_ptr blockPtr(new BlockWithTransactions()); if (!lbs->getBlockByHash(blockId, blockPtr->block)) { logger(DEBUGGING) << "Can't find block: " << blockId; return std::unique_ptr(nullptr); } blockPtr->transactions.reserve(blockPtr->block.transactionHashes.size()); std::vector missedTxs; lbs->getTransactions(blockPtr->block.transactionHashes, blockPtr->transactions, missedTxs, true); assert(missedTxs.empty() || !lbs->isBlockInMainChain(blockId)); //if can't find transaction for blockchain block -> error if (!missedTxs.empty()) { logger(DEBUGGING) << "Can't find transactions for block: " << blockId; return std::unique_ptr(nullptr); } return std::move(blockPtr); } bool core::addMessageQueue(MessageQueue& messageQueue) { return m_blockchain.addMessageQueue(messageQueue); } bool core::removeMessageQueue(MessageQueue& messageQueue) { return m_blockchain.removeMessageQueue(messageQueue); } }