// 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 .
#pragma once
#include
#include "google/sparse_hash_set"
#include "google/sparse_hash_map"
#include "Common/ObserverManager.h"
#include "Common/Util.h"
#include "CryptoNoteCore/BlockIndex.h"
#include "CryptoNoteCore/Checkpoints.h"
#include "CryptoNoteCore/Currency.h"
#include "CryptoNoteCore/IBlockchainStorageObserver.h"
#include "CryptoNoteCore/ITransactionValidator.h"
#include "CryptoNoteCore/SwappedVector.h"
#include "CryptoNoteCore/UpgradeDetector.h"
#include "CryptoNoteCore/CryptoNoteFormatUtils.h"
#include "CryptoNoteCore/TransactionPool.h"
#include "CryptoNoteCore/BlockchainIndices.h"
#include "CryptoNoteCore/MessageQueue.h"
#include "CryptoNoteCore/BlockchainMessages.h"
#include "CryptoNoteCore/IntrusiveLinkedList.h"
#include
#undef ERROR
namespace CryptoNote {
struct NOTIFY_REQUEST_GET_OBJECTS_request;
struct NOTIFY_RESPONSE_GET_OBJECTS_request;
struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request;
struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response;
struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount;
using CryptoNote::BlockInfo;
class Blockchain : public CryptoNote::ITransactionValidator {
public:
Blockchain(const Currency& currency, tx_memory_pool& tx_pool, Logging::ILogger& logger);
bool addObserver(IBlockchainStorageObserver* observer);
bool removeObserver(IBlockchainStorageObserver* observer);
// ITransactionValidator
virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) override;
virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) override;
virtual bool haveSpentKeyImages(const CryptoNote::Transaction& tx) override;
virtual bool checkTransactionSize(size_t blobSize) override;
bool init() { return init(Tools::getDefaultDataDirectory(), true); }
bool init(const std::string& config_folder, bool load_existing);
bool deinit();
bool getLowerBound(uint64_t timestamp, uint64_t startOffset, uint32_t& height);
std::vector getBlockIds(uint32_t startHeight, uint32_t maxCount);
void setCheckpoints(Checkpoints&& chk_pts) { m_checkpoints = chk_pts; }
bool getBlocks(uint32_t start_offset, uint32_t count, std::list& blocks, std::list& txs);
bool getBlocks(uint32_t start_offset, uint32_t count, std::list& blocks);
bool getAlternativeBlocks(std::list& blocks);
uint32_t getAlternativeBlocksCount();
Crypto::Hash getBlockIdByHeight(uint32_t height);
bool getBlockByHash(const Crypto::Hash &h, Block &blk);
bool getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight);
template void serialize(archive_t & ar, const unsigned int version);
bool haveTransaction(const Crypto::Hash &id);
bool haveTransactionKeyImagesAsSpent(const Transaction &tx);
uint32_t getCurrentBlockchainHeight(); //TODO rename to getCurrentBlockchainSize
Crypto::Hash getTailId();
Crypto::Hash getTailId(uint32_t& height);
difficulty_type getDifficultyForNextBlock();
uint64_t getCoinsInCirculation();
uint8_t getBlockMajorVersionForHeight(uint32_t height) const;
bool addNewBlock(const Block& bl_, block_verification_context& bvc);
bool resetAndSetGenesisBlock(const Block& b);
bool haveBlock(const Crypto::Hash& id);
size_t getTotalTransactions();
std::vector buildSparseChain();
std::vector buildSparseChain(const Crypto::Hash& startBlockId);
uint32_t findBlockchainSupplement(const std::vector& qblock_ids); // !!!!
std::vector findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount,
uint32_t& totalBlockCount, uint32_t& startBlockIndex);
bool handleGetObjects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp); //Deprecated. Should be removed with CryptoNoteProtocolHandler.
bool getRandomOutsByAmount(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res);
bool getBackwardBlocksSize(size_t from_height, std::vector& sz, size_t count);
bool getTransactionOutputGlobalIndexes(const Crypto::Hash& tx_id, std::vector& indexs);
bool get_out_by_msig_gindex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out);
bool checkTransactionInputs(const Transaction& tx, uint32_t& pmax_used_block_height, Crypto::Hash& max_used_block_id, BlockInfo* tail = 0);
uint64_t getCurrentCumulativeBlocksizeLimit();
bool isStoringBlockchain(){return m_is_blockchain_storing;}
uint64_t blockDifficulty(size_t i);
bool getBlockContainingTransaction(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight);
bool getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins);
bool getBlockSize(const Crypto::Hash& hash, size_t& size);
bool getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair& outputReference);
bool getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions);
bool getOrphanBlockIdsByHeight(uint32_t height, std::vector& blockHashes);
bool getBlockIdsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& hashes, uint32_t& blocksNumberWithinTimestamps);
bool getTransactionIdsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactionHashes);
bool isBlockInMainChain(const Crypto::Hash& blockId);
template bool scanOutputKeysForIndexes(const KeyInput& tx_in_to_key, visitor_t& vis, uint32_t* pmax_related_block_height = NULL);
bool addMessageQueue(MessageQueue& messageQueue);
bool removeMessageQueue(MessageQueue& messageQueue);
template
bool getBlocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) {
std::lock_guard lk(m_blockchain_lock);
for (const auto& bl_id : block_ids) {
uint32_t height = 0;
if (!m_blockIndex.getBlockHeight(bl_id, height)) {
missed_bs.push_back(bl_id);
} else {
if (!(height < m_blocks.size())) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: bl_id=" << Common::podToHex(bl_id)
<< " have index record with offset=" << height << ", bigger then m_blocks.size()=" << m_blocks.size(); return false; }
blocks.push_back(m_blocks[height].bl);
}
}
return true;
}
template
void getBlockchainTransactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) {
std::lock_guard bcLock(m_blockchain_lock);
for (const auto& tx_id : txs_ids) {
auto it = m_transactionMap.find(tx_id);
if (it == m_transactionMap.end()) {
missed_txs.push_back(tx_id);
} else {
txs.push_back(transactionByIndex(it->second).tx);
}
}
}
template
void getTransactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool checkTxPool = false) {
if (checkTxPool){
std::lock_guard txLock(m_tx_pool);
getBlockchainTransactions(txs_ids, txs, missed_txs);
auto poolTxIds = std::move(missed_txs);
missed_txs.clear();
m_tx_pool.getTransactions(poolTxIds, txs, missed_txs);
} else {
getBlockchainTransactions(txs_ids, txs, missed_txs);
}
}
//debug functions
void print_blockchain(uint64_t start_index, uint64_t end_index);
void print_blockchain_index();
void print_blockchain_outs(const std::string& file);
struct TransactionIndex {
uint32_t block;
uint16_t transaction;
void serialize(ISerializer& s) {
s(block, "block");
s(transaction, "tx");
}
};
private:
struct MultisignatureOutputUsage {
TransactionIndex transactionIndex;
uint16_t outputIndex;
bool isUsed;
void serialize(ISerializer& s) {
s(transactionIndex, "txindex");
s(outputIndex, "outindex");
s(isUsed, "used");
}
};
struct TransactionEntry {
Transaction tx;
std::vector m_global_output_indexes;
void serialize(ISerializer& s) {
s(tx, "tx");
s(m_global_output_indexes, "indexes");
}
};
struct BlockEntry {
Block bl;
uint32_t height;
uint64_t block_cumulative_size;
difficulty_type cumulative_difficulty;
uint64_t already_generated_coins;
std::vector transactions;
void serialize(ISerializer& s) {
s(bl, "block");
s(height, "height");
s(block_cumulative_size, "block_cumulative_size");
s(cumulative_difficulty, "cumulative_difficulty");
s(already_generated_coins, "already_generated_coins");
s(transactions, "transactions");
}
};
typedef google::sparse_hash_set key_images_container;
typedef std::unordered_map blocks_ext_by_hash;
typedef google::sparse_hash_map>> outputs_container; //Crypto::Hash - tx hash, size_t - index of out in transaction
typedef google::sparse_hash_map> MultisignatureOutputsContainer;
const Currency& m_currency;
tx_memory_pool& m_tx_pool;
std::recursive_mutex m_blockchain_lock; // TODO: add here reader/writer lock
Crypto::cn_context m_cn_context;
Tools::ObserverManager m_observerManager;
key_images_container m_spent_keys;
size_t m_current_block_cumul_sz_limit;
blocks_ext_by_hash m_alternative_chains; // Crypto::Hash -> block_extended_info
outputs_container m_outputs;
std::string m_config_folder;
Checkpoints m_checkpoints;
std::atomic m_is_in_checkpoint_zone;
std::atomic m_is_blockchain_storing;
typedef SwappedVector Blocks;
typedef std::unordered_map BlockMap;
typedef std::unordered_map TransactionMap;
typedef BasicUpgradeDetector UpgradeDetector;
friend class BlockCacheSerializer;
friend class BlockchainIndicesSerializer;
Blocks m_blocks;
CryptoNote::BlockIndex m_blockIndex;
TransactionMap m_transactionMap;
MultisignatureOutputsContainer m_multisignatureOutputs;
UpgradeDetector m_upgradeDetector;
PaymentIdIndex m_paymentIdIndex;
TimestampBlocksIndex m_timestampIndex;
GeneratedTransactionsIndex m_generatedTransactionsIndex;
OrphanBlocksIndex m_orthanBlocksIndex;
IntrusiveLinkedList> m_messageQueueList;
Logging::LoggerRef logger;
void rebuildCache();
bool storeCache();
bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain);
bool handle_alternative_block(const Block& b, const Crypto::Hash& id, block_verification_context& bvc, bool sendNewAlternativeBlockMessage = true);
difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei);
bool prevalidate_miner_transaction(const Block& b, uint32_t height);
bool validate_miner_transaction(const Block& b, uint32_t height, size_t cumulativeBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, uint64_t& reward, int64_t& emissionChange);
bool rollback_blockchain_switching(std::list& original_chain, size_t rollback_height);
bool get_last_n_blocks_sizes(std::vector& sz, size_t count);
bool add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount& result_outs, uint64_t amount, size_t i);
bool is_tx_spendtime_unlocked(uint64_t unlock_time);
size_t find_end_of_allowed_index(const std::vector>& amount_outs);
bool check_block_timestamp_main(const Block& b);
bool check_block_timestamp(std::vector timestamps, const Block& b);
uint64_t get_adjusted_time();
bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps);
bool checkBlockVersion(const Block& b, const Crypto::Hash& blockHash);
bool checkParentBlockSize(const Block& b, const Crypto::Hash& blockHash);
bool checkCumulativeBlockSize(const Crypto::Hash& blockId, size_t cumulativeBlockSize, uint64_t height);
std::vector doBuildSparseChain(const Crypto::Hash& startBlockId) const;
bool getBlockCumulativeSize(const Block& block, size_t& cumulativeSize);
bool update_next_comulative_size_limit();
bool check_tx_input(const KeyInput& txin, const Crypto::Hash& tx_prefix_hash, const std::vector& sig, uint32_t* pmax_related_block_height = NULL);
bool checkTransactionInputs(const Transaction& tx, const Crypto::Hash& tx_prefix_hash, uint32_t* pmax_used_block_height = NULL);
bool checkTransactionInputs(const Transaction& tx, uint32_t* pmax_used_block_height = NULL);
bool have_tx_keyimg_as_spent(const Crypto::KeyImage &key_im);
const TransactionEntry& transactionByIndex(TransactionIndex index);
bool pushBlock(const Block& blockData, block_verification_context& bvc);
bool pushBlock(const Block& blockData, const std::vector& transactions, block_verification_context& bvc);
bool pushBlock(BlockEntry& block);
void popBlock(const Crypto::Hash& blockHash);
bool pushTransaction(BlockEntry& block, const Crypto::Hash& transactionHash, TransactionIndex transactionIndex);
void popTransaction(const Transaction& transaction, const Crypto::Hash& transactionHash);
void popTransactions(const BlockEntry& block, const Crypto::Hash& minerTransactionHash);
bool validateInput(const MultisignatureInput& input, const Crypto::Hash& transactionHash, const Crypto::Hash& transactionPrefixHash, const std::vector& transactionSignatures);
bool storeBlockchainIndices();
bool loadBlockchainIndices();
bool loadTransactions(const Block& block, std::vector& transactions);
void saveTransactions(const std::vector& transactions);
void sendMessage(const BlockchainMessage& message);
friend class LockedBlockchainStorage;
};
class LockedBlockchainStorage: boost::noncopyable {
public:
LockedBlockchainStorage(Blockchain& bc)
: m_bc(bc), m_lock(bc.m_blockchain_lock) {}
Blockchain* operator -> () {
return &m_bc;
}
private:
Blockchain& m_bc;
std::lock_guard m_lock;
};
template bool Blockchain::scanOutputKeysForIndexes(const KeyInput& tx_in_to_key, visitor_t& vis, uint32_t* pmax_related_block_height) {
std::lock_guard lk(m_blockchain_lock);
auto it = m_outputs.find(tx_in_to_key.amount);
if (it == m_outputs.end() || !tx_in_to_key.outputIndexes.size())
return false;
std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.outputIndexes);
std::vector>& amount_outs_vec = it->second;
size_t count = 0;
for (uint64_t i : absolute_offsets) {
if(i >= amount_outs_vec.size() ) {
logger(Logging::INFO) << "Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1;
return false;
}
//auto tx_it = m_transactionMap.find(amount_outs_vec[i].first);
//if (!(tx_it != m_transactionMap.end())) { logger(ERROR, BRIGHT_RED) << "Wrong transaction id in output indexes: " << Common::podToHex(amount_outs_vec[i].first); return false; }
const TransactionEntry& tx = transactionByIndex(amount_outs_vec[i].first);
if (!(amount_outs_vec[i].second < tx.tx.outputs.size())) {
logger(Logging::ERROR, Logging::BRIGHT_RED)
<< "Wrong index in transaction outputs: "
<< amount_outs_vec[i].second << ", expected less then "
<< tx.tx.outputs.size();
return false;
}
if (!vis.handle_output(tx.tx, tx.tx.outputs[amount_outs_vec[i].second], amount_outs_vec[i].second)) {
logger(Logging::INFO) << "Failed to handle_output for output no = " << count << ", with absolute offset " << i;
return false;
}
if(count++ == absolute_offsets.size()-1 && pmax_related_block_height) {
if (*pmax_related_block_height < amount_outs_vec[i].first.block) {
*pmax_related_block_height = amount_outs_vec[i].first.block;
}
}
}
return true;
}
}