danicoin/src/cryptonote_core/blockchain_storage.cpp

2156 lines
84 KiB
C++
Raw Normal View History

2015-05-27 12:08:46 +00:00
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
2014-08-13 10:38:35 +00:00
//
// 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 <http://www.gnu.org/licenses/>.
2014-03-03 22:07:58 +00:00
2014-06-20 15:56:33 +00:00
#include "blockchain_storage.h"
2014-03-03 22:07:58 +00:00
#include <algorithm>
#include <cstdio>
2014-08-13 10:51:37 +00:00
2014-03-03 22:07:58 +00:00
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
2015-05-27 12:08:46 +00:00
#include <boost/foreach.hpp>
#include <boost/serialization/version.hpp>
#include <boost/utility/value_init.hpp>
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
#include "Common/boost_serialization_helper.h"
2015-07-15 12:23:00 +00:00
#include "Common/Math.h"
2015-05-27 12:08:46 +00:00
#include "Common/ShuffleGenerator.h"
#include "Common/StringTools.h"
2014-08-13 10:51:37 +00:00
2014-03-03 22:07:58 +00:00
#include "cryptonote_format_utils.h"
2014-08-13 10:51:37 +00:00
#include "rpc/core_rpc_server_commands_defs.h"
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
using namespace Logging;
2014-03-03 22:07:58 +00:00
2015-07-15 12:23:00 +00:00
#undef ERROR
2014-06-20 15:56:33 +00:00
namespace {
2015-07-15 12:23:00 +00:00
2015-05-27 12:08:46 +00:00
std::string appendPath(const std::string& path, const std::string& fileName) {
std::string result = path;
if (!result.empty()) {
result += '/';
2014-06-20 15:56:33 +00:00
}
2015-05-27 12:08:46 +00:00
result += fileName;
return result;
2014-06-20 15:56:33 +00:00
}
2015-05-27 12:08:46 +00:00
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
namespace std {
bool operator<(const crypto::hash& hash1, const crypto::hash& hash2) {
return memcmp(&hash1, &hash2, crypto::HASH_SIZE) < 0;
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
bool operator<(const crypto::key_image& keyImage1, const crypto::key_image& keyImage2) {
return memcmp(&keyImage1, &keyImage2, 32) < 0;
}
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
#define CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER 1
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
namespace CryptoNote {
class BlockCacheSerializer;
2014-06-20 15:56:33 +00:00
}
2015-05-27 12:08:46 +00:00
BOOST_CLASS_VERSION(CryptoNote::BlockCacheSerializer, CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER);
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
namespace CryptoNote
{
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
template<class Archive>
void blockchain_storage::TransactionIndex::serialize(Archive& archive, unsigned int version) {
2014-06-20 15:56:33 +00:00
archive & block;
archive & transaction;
}
2015-05-27 12:08:46 +00:00
template<class Archive>
void blockchain_storage::MultisignatureOutputUsage::serialize(Archive& archive, unsigned int version) {
2014-08-13 10:51:37 +00:00
archive & transactionIndex;
archive & outputIndex;
archive & isUsed;
}
2015-05-27 12:08:46 +00:00
class BlockCacheSerializer {
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
public:
BlockCacheSerializer(blockchain_storage& bs, const crypto::hash lastBlockHash, ILogger& logger) :
m_bs(bs), m_lastBlockHash(lastBlockHash), m_loaded(false), logger(logger, "BlockCacheSerializer") {
2014-06-20 15:56:33 +00:00
}
2015-05-27 12:08:46 +00:00
template<class Archive> void serialize(Archive& ar, unsigned int version) {
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
// ignore old versions, do rebuild
if (version < CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER)
return;
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
std::string operation;
if (Archive::is_loading::value) {
operation = "- loading ";
crypto::hash blockHash;
ar & blockHash;
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
if (blockHash != m_lastBlockHash) {
2014-08-13 10:51:37 +00:00
return;
}
2015-05-27 12:08:46 +00:00
} else {
operation = "- saving ";
ar & m_lastBlockHash;
}
logger(INFO) << operation << "block index...";
ar & m_bs.m_blockIndex;
2015-05-27 12:08:46 +00:00
logger(INFO) << operation << "transaction map...";
ar & m_bs.m_transactionMap;
2015-05-27 12:08:46 +00:00
logger(INFO) << operation << "spend keys...";
ar & m_bs.m_spent_keys;
2015-05-27 12:08:46 +00:00
logger(INFO) << operation << "outputs...";
ar & m_bs.m_outputs;
2015-05-27 12:08:46 +00:00
logger(INFO) << operation << "multi-signature outputs...";
ar & m_bs.m_multisignatureOutputs;
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
m_loaded = true;
}
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
bool loaded() const {
return m_loaded;
}
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
private:
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
LoggerRef logger;
bool m_loaded;
blockchain_storage& m_bs;
crypto::hash m_lastBlockHash;
};
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
blockchain_storage::blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool, ILogger& logger) :
logger(logger, "blockchain_storage"),
m_currency(currency),
m_tx_pool(tx_pool),
m_current_block_cumul_sz_limit(0),
m_is_in_checkpoint_zone(false),
m_is_blockchain_storing(false),
m_upgradeDetector(currency, m_blocks, BLOCK_MAJOR_VERSION_2, logger),
m_checkpoints(logger) {
2014-08-13 10:51:37 +00:00
m_outputs.set_deleted_key(0);
2015-05-27 12:08:46 +00:00
crypto::key_image nullImage = boost::value_initialized<decltype(nullImage)>();
2014-08-13 10:51:37 +00:00
m_spent_keys.set_deleted_key(nullImage);
}
bool blockchain_storage::addObserver(IBlockchainStorageObserver* observer) {
return m_observerManager.add(observer);
}
bool blockchain_storage::removeObserver(IBlockchainStorageObserver* observer) {
return m_observerManager.remove(observer);
}
2015-05-27 12:08:46 +00:00
bool blockchain_storage::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) {
2014-08-13 10:51:37 +00:00
return check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id);
}
2015-05-27 12:08:46 +00:00
bool blockchain_storage::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) {
2014-08-13 10:51:37 +00:00
BlockInfo tail;
//not the best implementation at this time, sorry :(
//check is ring_signature already checked ?
if (maxUsedBlock.empty()) {
//not checked, lets try to check
if (!lastFailed.empty() && get_current_blockchain_height() > lastFailed.height && get_block_id_by_height(lastFailed.height) == lastFailed.id) {
return false; //we already sure that this tx is broken for this height
}
2015-05-27 12:08:46 +00:00
2014-08-13 10:51:37 +00:00
if (!check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) {
lastFailed = tail;
return false;
}
2015-05-27 12:08:46 +00:00
} else {
2014-08-13 10:51:37 +00:00
if (maxUsedBlock.height >= get_current_blockchain_height()) {
return false;
}
if (get_block_id_by_height(maxUsedBlock.height) != maxUsedBlock.id) {
//if we already failed on this height and id, skip actual ring signature check
if (lastFailed.id == get_block_id_by_height(lastFailed.height)) {
return false;
}
//check ring signature again, it is possible (with very small chance) that this transaction become again valid
if (!check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) {
lastFailed = tail;
return false;
}
}
}
return true;
}
2015-05-27 12:08:46 +00:00
bool blockchain_storage::haveSpentKeyImages(const CryptoNote::Transaction& tx) {
2014-08-13 10:51:37 +00:00
return this->have_tx_keyimges_as_spent(tx);
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::have_tx(const crypto::hash &id) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
return m_transactionMap.find(id) != m_transactionMap.end();
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-03-03 22:07:58 +00:00
return m_spent_keys.find(key_im) != m_spent_keys.end();
}
2014-06-20 15:56:33 +00:00
uint64_t blockchain_storage::get_current_blockchain_height() {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-03-03 22:07:58 +00:00
return m_blocks.size();
}
2014-06-20 15:56:33 +00:00
2014-06-25 17:21:42 +00:00
bool blockchain_storage::init(const std::string& config_folder, bool load_existing) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-25 17:21:42 +00:00
if (!config_folder.empty() && !tools::create_directories_if_necessary(config_folder)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << "Failed to create data directory: " << m_config_folder;
return false;
}
2014-03-03 22:07:58 +00:00
m_config_folder = config_folder;
2014-06-25 17:21:42 +00:00
2014-08-13 10:51:37 +00:00
if (!m_blocks.open(appendPath(config_folder, m_currency.blocksFileName()), appendPath(config_folder, m_currency.blockIndexesFileName()), 1024)) {
2014-06-20 15:56:33 +00:00
return false;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
if (load_existing && !m_blocks.empty()) {
logger(INFO, BRIGHT_WHITE) << "Loading blockchain...";
BlockCacheSerializer loader(*this, get_block_hash(m_blocks.back().bl), logger.getLogger());
tools::unserialize_obj_from_file(loader, appendPath(config_folder, m_currency.blocksCacheFileName()));
if (!loader.loaded()) {
logger(WARNING, BRIGHT_YELLOW) << "No actual blockchain cache found, rebuilding internal structures...";
std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now();
m_blockIndex.clear();
m_transactionMap.clear();
m_spent_keys.clear();
m_outputs.clear();
m_multisignatureOutputs.clear();
for (uint32_t b = 0; b < m_blocks.size(); ++b) {
if (b % 1000 == 0) {
logger(INFO, BRIGHT_WHITE) << "Height " << b << " of " << m_blocks.size();
}
const BlockEntry& block = m_blocks[b];
crypto::hash blockHash = get_block_hash(block.bl);
m_blockIndex.push(blockHash);
for (uint16_t t = 0; t < block.transactions.size(); ++t) {
const TransactionEntry& transaction = block.transactions[t];
crypto::hash transactionHash = get_transaction_hash(transaction.tx);
TransactionIndex transactionIndex = {b, t};
m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex));
// process inputs
for (auto& i : transaction.tx.vin) {
if (i.type() == typeid(TransactionInputToKey)) {
m_spent_keys.insert(::boost::get<TransactionInputToKey>(i).keyImage);
} else if (i.type() == typeid(TransactionInputMultisignature)) {
auto out = ::boost::get<TransactionInputMultisignature>(i);
m_multisignatureOutputs[out.amount][out.outputIndex].isUsed = true;
2014-06-20 15:56:33 +00:00
}
2015-05-27 12:08:46 +00:00
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
// process outputs
for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) {
const auto& out = transaction.tx.vout[o];
if (out.target.type() == typeid(TransactionOutputToKey)) {
m_outputs[out.amount].push_back(std::make_pair<>(transactionIndex, o));
} else if (out.target.type() == typeid(TransactionOutputMultisignature)) {
MultisignatureOutputUsage usage = {transactionIndex, o, false};
m_multisignatureOutputs[out.amount].push_back(usage);
2014-06-25 17:21:42 +00:00
}
2014-06-20 15:56:33 +00:00
}
}
2014-06-25 17:21:42 +00:00
}
2015-05-27 12:08:46 +00:00
std::chrono::duration<double> duration = std::chrono::steady_clock::now() - timePoint;
logger(INFO, BRIGHT_WHITE) << "Rebuilding internal structures took: " << duration.count();
2014-06-20 15:56:33 +00:00
}
2014-06-25 17:21:42 +00:00
} else {
m_blocks.clear();
2014-06-20 15:56:33 +00:00
}
if (m_blocks.empty()) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE)
<< "Blockchain not loaded, generating genesis block.";
block_verification_context bvc =
boost::value_initialized<block_verification_context>();
2014-08-13 10:51:37 +00:00
add_new_block(m_currency.genesisBlock(), bvc);
2015-05-27 12:08:46 +00:00
if (bvc.m_verifivation_failed) {
logger(ERROR, BRIGHT_RED) << "Failed to add genesis block to blockchain";
return false;
}
2014-08-13 10:51:37 +00:00
} else {
crypto::hash firstBlockHash = get_block_hash(m_blocks[0].bl);
2015-05-27 12:08:46 +00:00
if (!(firstBlockHash == m_currency.genesisBlockHash())) {
logger(ERROR, BRIGHT_RED) << "Failed to init: genesis block mismatch. "
"Probably you set --testnet flag with data "
"dir with non-test blockchain or another "
"network.";
return false;
}
2014-08-13 10:51:37 +00:00
}
if (!m_upgradeDetector.init()) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << "Failed to initialize upgrade detector";
2014-08-13 10:51:37 +00:00
return false;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-08-14 15:41:44 +00:00
update_next_comulative_size_limit();
2014-03-03 22:07:58 +00:00
uint64_t timestamp_diff = time(NULL) - m_blocks.back().bl.timestamp;
2014-08-13 10:51:37 +00:00
if (!m_blocks.back().bl.timestamp) {
2014-03-03 22:07:58 +00:00
timestamp_diff = time(NULL) - 1341378000;
2014-08-13 10:51:37 +00:00
}
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_GREEN)
<< "Blockchain initialized. last block: " << m_blocks.size() - 1 << ", "
<< Common::timeIntervalToString(timestamp_diff)
<< " time ago, current difficulty: " << get_difficulty_for_next_block();
2014-03-03 22:07:58 +00:00
return true;
}
2014-03-20 11:46:11 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::storeCache() {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) << "Saving blockchain...";
BlockCacheSerializer ser(*this, get_tail_id(), logger.getLogger());
2014-08-13 10:51:37 +00:00
if (!tools::serialize_obj_to_file(ser, appendPath(m_config_folder, m_currency.blocksCacheFileName()))) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << "Failed to save blockchain cache";
2014-08-13 10:51:37 +00:00
return false;
2014-03-03 22:07:58 +00:00
}
return true;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::deinit() {
2014-08-13 10:51:37 +00:00
storeCache();
return true;
2014-03-03 22:07:58 +00:00
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage::reset_and_set_genesis_block(const Block& b) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-03-03 22:07:58 +00:00
m_blocks.clear();
2014-08-13 10:51:37 +00:00
m_blockIndex.clear();
2014-06-20 15:56:33 +00:00
m_transactionMap.clear();
m_spent_keys.clear();
2014-03-03 22:07:58 +00:00
m_alternative_chains.clear();
m_outputs.clear();
block_verification_context bvc = boost::value_initialized<block_verification_context>();
add_new_block(b, bvc);
return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
}
2014-06-20 15:56:33 +00:00
crypto::hash blockchain_storage::get_tail_id(uint64_t& height) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
height = get_current_blockchain_height() - 1;
2014-03-03 22:07:58 +00:00
return get_tail_id();
}
2014-06-20 15:56:33 +00:00
crypto::hash blockchain_storage::get_tail_id() {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-08-13 10:51:37 +00:00
return m_blockIndex.getTailId();
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-15 12:23:00 +00:00
bool blockchain_storage::getPoolChanges(const crypto::hash& tailBlockId, const std::vector<crypto::hash>& knownTxsIds,
std::vector<Transaction>& addedTxs, std::vector<crypto::hash>& deletedTxsIds) {
// Locks are necessary in order to make sure blockchain isn't changed between get_tail_id() and getPoolChanges()
std::lock_guard<decltype(m_tx_pool)> txPoolLock(m_tx_pool);
std::lock_guard<decltype(m_blockchain_lock)> blockchainLock(m_blockchain_lock);
2015-05-27 12:08:46 +00:00
2015-07-15 12:23:00 +00:00
if (tailBlockId != get_tail_id()) {
return false;
}
2015-07-15 12:23:00 +00:00
getPoolChanges(knownTxsIds, addedTxs, deletedTxsIds);
return true;
}
void blockchain_storage::getPoolChanges(const std::vector<crypto::hash>& knownTxsIds, std::vector<Transaction>& addedTxs,
std::vector<crypto::hash>& deletedTxsIds) {
std::lock_guard<decltype(m_tx_pool)> txPoolLock(m_tx_pool);
std::vector<crypto::hash> addedTxsIds;
m_tx_pool.get_difference(knownTxsIds, addedTxsIds, deletedTxsIds);
std::vector<crypto::hash> misses;
2015-07-15 12:23:00 +00:00
m_tx_pool.getTransactions(addedTxsIds, addedTxs, misses);
assert(misses.empty());
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::get_short_chain_history(std::list<crypto::hash>& ids) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-08-13 10:51:37 +00:00
return m_blockIndex.getShortChainHistory(ids);
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-08-13 10:51:37 +00:00
return m_blockIndex.getBlockId(height);
2014-03-03 22:07:58 +00:00
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage::get_block_by_hash(const crypto::hash& blockHash, Block& b) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-08-13 10:51:37 +00:00
uint64_t height = 0;
if (m_blockIndex.getBlockHeight(blockHash, height)) {
b = m_blocks[height].bl;
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
auto blockByHashIterator = m_alternative_chains.find(blockHash);
if (blockByHashIterator != m_alternative_chains.end()) {
b = blockByHashIterator->second.bl;
2014-03-03 22:07:58 +00:00
return true;
}
return false;
}
2014-06-20 15:56:33 +00:00
difficulty_type blockchain_storage::get_difficulty_for_next_block() {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-03-03 22:07:58 +00:00
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> commulative_difficulties;
2014-08-13 10:51:37 +00:00
size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast<uint64_t>(m_currency.difficultyBlocksCount()));
2014-06-20 15:56:33 +00:00
if (offset == 0) {
++offset;
}
for (; offset < m_blocks.size(); offset++) {
2014-03-03 22:07:58 +00:00
timestamps.push_back(m_blocks[offset].bl.timestamp);
commulative_difficulties.push_back(m_blocks[offset].cumulative_difficulty);
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
return m_currency.nextDifficulty(timestamps, commulative_difficulties);
}
uint64_t blockchain_storage::getCoinsInCirculation() {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-08-13 10:51:37 +00:00
if (m_blocks.empty()) {
return 0;
} else {
return m_blocks.back().already_generated_coins;
}
}
uint8_t blockchain_storage::get_block_major_version_for_height(uint64_t height) const {
return height > m_upgradeDetector.upgradeHeight() ? m_upgradeDetector.targetVersion() : BLOCK_MAJOR_VERSION_1;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
bool blockchain_storage::rollback_blockchain_switching(std::list<Block> &original_chain, size_t rollback_height) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
// remove failed subchain
for (size_t i = m_blocks.size() - 1; i >= rollback_height; i--) {
2014-06-20 15:56:33 +00:00
popBlock(get_block_hash(m_blocks.back().bl));
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
// return back original chain
for(auto &bl : original_chain) {
block_verification_context bvc =
boost::value_initialized<block_verification_context>();
2014-06-20 15:56:33 +00:00
bool r = pushBlock(bl, bvc);
2015-05-27 12:08:46 +00:00
if (!(r && bvc.m_added_to_main_chain)) {
logger(ERROR, BRIGHT_RED) << "PANIC!!! failed to add (again) block while "
"chain switching during the rollback!";
return false;
}
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) << "Rollback success.";
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
if (!(alt_chain.size())) {
logger(ERROR, BRIGHT_RED) << "switch_to_alternative_blockchain: empty chain passed";
return false;
}
2014-03-03 22:07:58 +00:00
size_t split_height = alt_chain.front()->second.height;
2015-05-27 12:08:46 +00:00
if (!(m_blocks.size() > split_height)) {
logger(ERROR, BRIGHT_RED) << "switch_to_alternative_blockchain: blockchain size is lower than split height";
return false;
}
2014-03-03 22:07:58 +00:00
//disconnecting old chain
2014-08-13 10:51:37 +00:00
std::list<Block> disconnected_chain;
2014-06-20 15:56:33 +00:00
for (size_t i = m_blocks.size() - 1; i >= split_height; i--) {
2014-08-13 10:51:37 +00:00
Block b = m_blocks[i].bl;
2014-06-20 15:56:33 +00:00
popBlock(get_block_hash(b));
2015-05-27 12:08:46 +00:00
//if (!(r)) { logger(ERROR, BRIGHT_RED) << "failed to remove block on chain switching"; return false; }
2014-03-03 22:07:58 +00:00
disconnected_chain.push_front(b);
}
//connecting new alternative chain
2014-06-20 15:56:33 +00:00
for (auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++) {
2014-03-03 22:07:58 +00:00
auto ch_ent = *alt_ch_iter;
block_verification_context bvc = boost::value_initialized<block_verification_context>();
2014-06-20 15:56:33 +00:00
bool r = pushBlock(ch_ent->second.bl, bvc);
if (!r || !bvc.m_added_to_main_chain) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) << "Failed to switch to alternative blockchain";
2014-03-03 22:07:58 +00:00
rollback_blockchain_switching(disconnected_chain, split_height);
2014-06-20 15:56:33 +00:00
//add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl));
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) << "The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl);
2014-03-03 22:07:58 +00:00
m_alternative_chains.erase(ch_ent);
2014-06-20 15:56:33 +00:00
for (auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++) {
2014-03-03 22:07:58 +00:00
//block_verification_context bvc = boost::value_initialized<block_verification_context>();
2014-06-20 15:56:33 +00:00
//add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first);
2014-03-03 22:07:58 +00:00
m_alternative_chains.erase(*alt_ch_to_orph_iter);
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return false;
}
}
2014-06-20 15:56:33 +00:00
if (!discard_disconnected_chain) {
2014-05-15 16:40:40 +00:00
//pushing old chain as alternative chain
2014-06-20 15:56:33 +00:00
for (auto& old_ch_ent : disconnected_chain) {
2014-05-15 16:40:40 +00:00
block_verification_context bvc = boost::value_initialized<block_verification_context>();
bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc);
2014-06-20 15:56:33 +00:00
if (!r) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << ("Failed to push ex-main chain blocks to alternative chain ");
2014-05-15 16:40:40 +00:00
rollback_blockchain_switching(disconnected_chain, split_height);
return false;
}
2014-03-03 22:07:58 +00:00
}
}
//removing all_chain entries from alternative chain
2014-06-20 15:56:33 +00:00
for (auto ch_ent : alt_chain) {
2014-03-03 22:07:58 +00:00
m_alternative_chains.erase(ch_ent);
}
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_GREEN) << "REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_blocks.size();
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, BlockEntry& bei) {
2014-03-03 22:07:58 +00:00
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> commulative_difficulties;
2014-08-13 10:51:37 +00:00
if (alt_chain.size() < m_currency.difficultyBlocksCount()) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-03-03 22:07:58 +00:00
size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height;
2014-08-13 10:51:37 +00:00
size_t main_chain_count = m_currency.difficultyBlocksCount() - std::min(m_currency.difficultyBlocksCount(), alt_chain.size());
2014-03-03 22:07:58 +00:00
main_chain_count = std::min(main_chain_count, main_chain_stop_offset);
size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count;
2014-06-20 15:56:33 +00:00
if (!main_chain_start_offset)
2014-03-03 22:07:58 +00:00
++main_chain_start_offset; //skip genesis block
2014-06-20 15:56:33 +00:00
for (; main_chain_start_offset < main_chain_stop_offset; ++main_chain_start_offset) {
2014-03-03 22:07:58 +00:00
timestamps.push_back(m_blocks[main_chain_start_offset].bl.timestamp);
commulative_difficulties.push_back(m_blocks[main_chain_start_offset].cumulative_difficulty);
}
2015-05-27 12:08:46 +00:00
if (!((alt_chain.size() + timestamps.size()) <= m_currency.difficultyBlocksCount())) {
logger(ERROR, BRIGHT_RED) << "Internal error, alt_chain.size()[" << alt_chain.size() << "] + timestamps.size()[" << timestamps.size() <<
"] NOT <= m_currency.difficultyBlocksCount()[" << m_currency.difficultyBlocksCount() << ']'; return false;
}
2014-06-20 15:56:33 +00:00
for (auto it : alt_chain) {
2014-03-03 22:07:58 +00:00
timestamps.push_back(it->second.bl.timestamp);
commulative_difficulties.push_back(it->second.cumulative_difficulty);
}
2014-06-20 15:56:33 +00:00
} else {
2014-08-13 10:51:37 +00:00
timestamps.resize(std::min(alt_chain.size(), m_currency.difficultyBlocksCount()));
commulative_difficulties.resize(std::min(alt_chain.size(), m_currency.difficultyBlocksCount()));
2014-03-03 22:07:58 +00:00
size_t count = 0;
2014-06-20 15:56:33 +00:00
size_t max_i = timestamps.size() - 1;
BOOST_REVERSE_FOREACH(auto it, alt_chain) {
2014-03-03 22:07:58 +00:00
timestamps[max_i - count] = it->second.bl.timestamp;
commulative_difficulties[max_i - count] = it->second.cumulative_difficulty;
count++;
2014-08-13 10:51:37 +00:00
if (count >= m_currency.difficultyBlocksCount()) {
2014-03-03 22:07:58 +00:00
break;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
}
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
return m_currency.nextDifficulty(timestamps, commulative_difficulties);
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::prevalidate_miner_transaction(const Block& b, uint64_t height) {
2015-05-27 12:08:46 +00:00
if (!(b.minerTx.vin.size() == 1)) {
logger(ERROR, BRIGHT_RED)
<< "coinbase transaction in the block has no inputs";
return false;
}
if (!(b.minerTx.vin[0].type() == typeid(TransactionInputGenerate))) {
logger(ERROR, BRIGHT_RED)
<< "coinbase transaction in the block has the wrong type";
return false;
}
2014-08-13 10:51:37 +00:00
if (boost::get<TransactionInputGenerate>(b.minerTx.vin[0]).height != height) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_RED) << "The miner transaction in block has invalid height: " <<
boost::get<TransactionInputGenerate>(b.minerTx.vin[0]).height << ", expected: " << height;
2014-03-03 22:07:58 +00:00
return false;
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
if (!(b.minerTx.unlockTime == height + m_currency.minedMoneyUnlockWindow())) {
logger(ERROR, BRIGHT_RED)
<< "coinbase transaction transaction have wrong unlock time="
<< b.minerTx.unlockTime << ", expected "
<< height + m_currency.minedMoneyUnlockWindow();
return false;
}
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
if (!check_outs_overflow(b.minerTx)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_RED) << "miner transaction have money overflow in block " << get_block_hash(b);
2014-03-03 22:07:58 +00:00
return false;
}
return true;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::validate_miner_transaction(const Block& b, uint64_t height, size_t cumulativeBlockSize,
2015-05-27 12:08:46 +00:00
uint64_t alreadyGeneratedCoins, uint64_t fee,
uint64_t& reward, int64_t& emissionChange) {
2014-08-13 10:51:37 +00:00
uint64_t minerReward = 0;
for (auto& o : b.minerTx.vout) {
minerReward += o.amount;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
std::vector<size_t> lastBlocksSizes;
get_last_n_blocks_sizes(lastBlocksSizes, m_currency.rewardBlocksWindow());
2015-07-15 12:23:00 +00:00
size_t blocksSizeMedian = Common::medianValue(lastBlocksSizes);
2014-08-13 10:51:37 +00:00
bool penalizeFee = get_block_major_version_for_height(height) > BLOCK_MAJOR_VERSION_1;
if (!m_currency.getBlockReward(blocksSizeMedian, cumulativeBlockSize, alreadyGeneratedCoins, fee, penalizeFee, reward, emissionChange)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) << "block size " << cumulativeBlockSize << " is bigger than allowed for this blockchain";
2014-03-03 22:07:58 +00:00
return false;
}
2015-05-27 12:08:46 +00:00
2014-08-13 10:51:37 +00:00
if (minerReward > reward) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << "Coinbase transaction spend too much money: " << m_currency.formatAmount(minerReward) <<
", block reward is " << m_currency.formatAmount(reward);
2014-03-03 22:07:58 +00:00
return false;
2014-08-13 10:51:37 +00:00
} else if (minerReward < reward) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << "Coinbase transaction doesn't use full amount of block reward: spent " <<
m_currency.formatAmount(minerReward) << ", block reward is " << m_currency.formatAmount(reward);
2014-03-03 22:07:58 +00:00
return false;
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector<size_t>& sz, size_t count) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
if (!(from_height < m_blocks.size())) {
logger(ERROR, BRIGHT_RED)
<< "Internal error: get_backward_blocks_sizes called with from_height="
<< from_height << ", blockchain height = " << m_blocks.size();
return false;
}
2014-06-20 15:56:33 +00:00
size_t start_offset = (from_height + 1) - std::min((from_height + 1), count);
for (size_t i = start_offset; i != from_height + 1; i++) {
2014-03-03 22:07:58 +00:00
sz.push_back(m_blocks[i].block_cumulative_size);
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
if (!m_blocks.size()) {
2014-03-03 22:07:58 +00:00
return true;
2014-06-20 15:56:33 +00:00
}
return get_backward_blocks_sizes(m_blocks.size() - 1, sz, count);
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
uint64_t blockchain_storage::get_current_comulative_blocksize_limit() {
2014-04-02 16:00:17 +00:00
return m_current_block_cumul_sz_limit;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
bool blockchain_storage::create_block_template(Block& b, const AccountPublicAddress& miner_address, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce) {
2014-04-02 16:00:17 +00:00
size_t median_size;
uint64_t already_generated_coins;
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
{
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
height = static_cast<uint32_t>(m_blocks.size());
diffic = get_difficulty_for_next_block();
if (!(diffic)) {
logger(ERROR, BRIGHT_RED) << "difficulty overhead.";
return false;
}
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
b = boost::value_initialized<Block>();
b.majorVersion = get_block_major_version_for_height(height);
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
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;
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
b.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1;
b.parentBlock.majorVersion = BLOCK_MINOR_VERSION_0;
b.parentBlock.numberOfTransactions = 1;
tx_extra_merge_mining_tag mm_tag = boost::value_initialized<decltype(mm_tag)>();
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
if (!append_mm_tag_to_extra(b.parentBlock.minerTx.extra, mm_tag)) {
logger(ERROR, BRIGHT_RED) << "Failed to append merge mining tag to extra of the parent block miner transaction";
return false;
}
}
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
b.prevId = get_tail_id();
b.timestamp = time(NULL);
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
median_size = m_current_block_cumul_sz_limit / 2;
already_generated_coins = m_blocks.back().already_generated_coins;
}
2014-03-03 22:07:58 +00:00
2014-04-02 16:00:17 +00:00
size_t txs_size;
uint64_t fee;
2014-08-13 10:51:37 +00:00
if (!m_tx_pool.fill_block_template(b, median_size, m_currency.maxBlockCumulativeSize(height), already_generated_coins,
2015-05-27 12:08:46 +00:00
txs_size, fee)) {
2014-04-02 16:00:17 +00:00
return false;
}
2014-06-20 15:56:33 +00:00
2014-04-29 16:26:45 +00:00
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
size_t real_txs_size = 0;
uint64_t real_fee = 0;
CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock);
2014-08-13 10:51:37 +00:00
for (crypto::hash &cur_hash : b.txHashes) {
2014-04-29 16:26:45 +00:00
auto cur_res = m_tx_pool.m_transactions.find(cur_hash);
if (cur_res == m_tx_pool.m_transactions.end()) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << "Creating block template: error: transaction not found";
2014-04-29 16:26:45 +00:00
continue;
}
tx_memory_pool::tx_details &cur_tx = cur_res->second;
real_txs_size += cur_tx.blob_size;
real_fee += cur_tx.fee;
if (cur_tx.blob_size != get_object_blobsize(cur_tx.tx)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << "Creating block template: error: invalid transaction size";
2014-04-29 16:26:45 +00:00
}
uint64_t inputs_amount;
if (!get_inputs_money_amount(cur_tx.tx, inputs_amount)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << "Creating block template: error: cannot get inputs amount";
2014-04-29 16:26:45 +00:00
} else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << "Creating block template: error: invalid fee";
2014-04-29 16:26:45 +00:00
}
}
if (txs_size != real_txs_size) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << "Creating block template: error: wrongly calculated transaction size";
2014-04-29 16:26:45 +00:00
}
if (fee != real_fee) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << "Creating block template: error: wrongly calculated fee";
2014-04-29 16:26:45 +00:00
}
CRITICAL_REGION_END();
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) << "Creating block template: height " << height <<
2014-04-29 16:26:45 +00:00
", median size " << median_size <<
", already generated coins " << already_generated_coins <<
", transaction size " << txs_size <<
2015-05-27 12:08:46 +00:00
", fee " << fee;
2014-04-29 16:26:45 +00:00
#endif
2014-03-03 22:07:58 +00:00
/*
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
2014-06-20 15:56:33 +00:00
*/
2014-03-03 22:07:58 +00:00
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
2014-08-13 10:51:37 +00:00
bool penalizeFee = b.majorVersion > BLOCK_MAJOR_VERSION_1;
bool r = m_currency.constructMinerTx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.minerTx, ex_nonce, 11, penalizeFee);
2015-05-27 12:08:46 +00:00
if (!r) {
logger(ERROR, BRIGHT_RED) << "Failed to construc miner tx, first chance";
return false;
}
2014-08-13 10:51:37 +00:00
size_t cumulative_size = txs_size + get_object_blobsize(b.minerTx);
2014-04-29 16:26:45 +00:00
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) << "Creating block template: miner tx size " << get_object_blobsize(b.minerTx) <<
", cumulative size " << cumulative_size;
2014-04-29 16:26:45 +00:00
#endif
2014-04-02 16:00:17 +00:00
for (size_t try_count = 0; try_count != 10; ++try_count) {
2014-08-13 10:51:37 +00:00
r = m_currency.constructMinerTx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.minerTx, ex_nonce, 11, penalizeFee);
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to construc miner tx, second chance"; return false; }
2014-08-13 10:51:37 +00:00
size_t coinbase_blob_size = get_object_blobsize(b.minerTx);
2014-04-02 16:00:17 +00:00
if (coinbase_blob_size > cumulative_size - txs_size) {
cumulative_size = txs_size + coinbase_blob_size;
2014-04-29 16:26:45 +00:00
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) << "Creating block template: miner tx size " << coinbase_blob_size <<
", cumulative size " << cumulative_size << " is greater then before";
2014-04-29 16:26:45 +00:00
#endif
2014-03-03 22:07:58 +00:00
continue;
2014-04-02 16:00:17 +00:00
}
if (coinbase_blob_size < cumulative_size - txs_size) {
size_t delta = cumulative_size - txs_size - coinbase_blob_size;
2014-04-29 16:26:45 +00:00
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) << "Creating block template: miner tx size " << coinbase_blob_size <<
2014-04-29 16:26:45 +00:00
", cumulative size " << txs_size + coinbase_blob_size <<
2015-05-27 12:08:46 +00:00
" is less then before, adding " << delta << " zero bytes";
2014-04-29 16:26:45 +00:00
#endif
2014-08-13 10:51:37 +00:00
b.minerTx.extra.insert(b.minerTx.extra.end(), delta, 0);
2014-04-02 16:00:17 +00:00
//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.
2014-08-13 10:51:37 +00:00
if (cumulative_size != txs_size + get_object_blobsize(b.minerTx)) {
2015-05-27 12:08:46 +00:00
if (!(cumulative_size + 1 == txs_size + get_object_blobsize(b.minerTx))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.minerTx)=" << get_object_blobsize(b.minerTx); return false; }
2014-08-13 10:51:37 +00:00
b.minerTx.extra.resize(b.minerTx.extra.size() - 1);
if (cumulative_size != txs_size + get_object_blobsize(b.minerTx)) {
2014-04-02 16:00:17 +00:00
//fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size
2015-05-27 12:08:46 +00:00
logger(TRACE, BRIGHT_RED) <<
"Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1;
2014-04-02 16:00:17 +00:00
cumulative_size += delta - 1;
continue;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
logger(DEBUGGING, BRIGHT_GREEN) <<
"Setting extra for block: " << b.minerTx.extra.size() << ", try_count=" << try_count;
2014-03-03 22:07:58 +00:00
}
}
2015-05-27 12:08:46 +00:00
if (!(cumulative_size == txs_size + get_object_blobsize(b.minerTx))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.minerTx)=" << get_object_blobsize(b.minerTx); return false; }
2014-04-29 16:26:45 +00:00
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) << "Creating block template: miner tx size " << coinbase_blob_size <<
", cumulative size " << cumulative_size << " is now good";
2014-04-29 16:26:45 +00:00
#endif
2014-04-02 16:00:17 +00:00
return true;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Failed to create_block_template with " << 10 << " tries";
2014-03-03 22:07:58 +00:00
return false;
}
2014-04-02 16:00:17 +00:00
2014-06-20 15:56:33 +00:00
bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps) {
2014-08-13 10:51:37 +00:00
if (timestamps.size() >= m_currency.timestampCheckWindow())
2014-03-03 22:07:58 +00:00
return true;
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-08-13 10:51:37 +00:00
size_t need_elements = m_currency.timestampCheckWindow() - timestamps.size();
2015-05-27 12:08:46 +00:00
if (!(start_top_height < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size(); return false; }
2014-06-20 15:56:33 +00:00
size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0;
2015-05-27 12:08:46 +00:00
do {
2014-03-03 22:07:58 +00:00
timestamps.push_back(m_blocks[start_top_height].bl.timestamp);
2014-06-20 15:56:33 +00:00
if (start_top_height == 0)
2014-03-03 22:07:58 +00:00
break;
--start_top_height;
2014-06-20 15:56:33 +00:00
} while (start_top_height != stop_offset);
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::handle_alternative_block(const Block& b, const crypto::hash& id, block_verification_context& bvc) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-03-03 22:07:58 +00:00
2014-05-15 16:40:40 +00:00
uint64_t block_height = get_block_height(b);
2014-06-20 15:56:33 +00:00
if (block_height == 0) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Block with id: " << Common::podToHex(id) << " (as alternative) have wrong miner transaction";
2014-05-15 16:40:40 +00:00
bvc.m_verifivation_failed = true;
return false;
}
2014-06-20 15:56:33 +00:00
if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) {
2015-05-27 12:08:46 +00:00
logger(TRACE) << "Block with id: " << id << std::endl <<
" can't be accepted for alternative chain, block height: " << block_height << std::endl <<
2015-05-27 12:08:46 +00:00
" blockchain height: " << get_current_blockchain_height();
2014-05-15 16:40:40 +00:00
bvc.m_verifivation_failed = true;
return false;
}
2014-08-13 10:51:37 +00:00
if (!checkBlockVersion(b, id)) {
bvc.m_verifivation_failed = true;
return false;
}
if (!checkParentBlockSize(b, id)) {
bvc.m_verifivation_failed = true;
return false;
}
size_t cumulativeSize;
if (!getBlockCumulativeSize(b, cumulativeSize)) {
2015-05-27 12:08:46 +00:00
logger(TRACE) << "Block with id: " << id << " has at least one unknown transaction. Cumulative size is calculated imprecisely";
2014-08-13 10:51:37 +00:00
}
if (!checkCumulativeBlockSize(id, cumulativeSize, block_height)) {
bvc.m_verifivation_failed = true;
return false;
}
2014-03-03 22:07:58 +00:00
//block is not related with head of main chain
//first of all - look in alternative chains container
2014-08-13 10:51:37 +00:00
uint64_t mainPrevHeight = 0;
const bool mainPrev = m_blockIndex.getBlockHeight(b.prevId, mainPrevHeight);
const auto it_prev = m_alternative_chains.find(b.prevId);
if (it_prev != m_alternative_chains.end() || mainPrev) {
2014-03-03 22:07:58 +00:00
//we have new block in alternative chain
//build alternative subchain, front -> mainchain, back -> alternative head
blocks_ext_by_hash::iterator alt_it = it_prev; //m_alternative_chains.find()
std::list<blocks_ext_by_hash::iterator> alt_chain;
std::vector<uint64_t> timestamps;
2014-06-20 15:56:33 +00:00
while (alt_it != m_alternative_chains.end()) {
2014-03-03 22:07:58 +00:00
alt_chain.push_front(alt_it);
timestamps.push_back(alt_it->second.bl.timestamp);
2014-08-13 10:51:37 +00:00
alt_it = m_alternative_chains.find(alt_it->second.bl.prevId);
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
if (alt_chain.size()) {
2014-03-03 22:07:58 +00:00
//make sure that it has right connection to main chain
2015-05-27 12:08:46 +00:00
if (!(m_blocks.size() > alt_chain.front()->second.height)) { logger(ERROR, BRIGHT_RED) << "main blockchain wrong height"; return false; }
2014-03-03 22:07:58 +00:00
crypto::hash h = null_hash;
get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h);
2015-05-27 12:08:46 +00:00
if (!(h == alt_chain.front()->second.bl.prevId)) { logger(ERROR, BRIGHT_RED) << "alternative chain have wrong connection to main chain"; return false; }
2014-03-03 22:07:58 +00:00
complete_timestamps_vector(alt_chain.front()->second.height - 1, timestamps);
2014-06-20 15:56:33 +00:00
} else {
2015-05-27 12:08:46 +00:00
if (!(mainPrev)) { logger(ERROR, BRIGHT_RED) << "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"; return false; }
2014-08-13 10:51:37 +00:00
complete_timestamps_vector(mainPrevHeight, timestamps);
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
//check timestamp correct
2014-06-20 15:56:33 +00:00
if (!check_block_timestamp(timestamps, b)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_RED) <<
"Block with id: " << id
<< ENDL << " for alternative chain, have invalid timestamp: " << b.timestamp;
2014-03-03 22:07:58 +00:00
//add_block_as_invalid(b, id);//do not add blocks to invalid storage before proof of work check was passed
bvc.m_verifivation_failed = true;
return false;
}
2014-08-13 10:51:37 +00:00
BlockEntry bei = boost::value_initialized<BlockEntry>();
2014-03-03 22:07:58 +00:00
bei.bl = b;
2014-08-13 10:51:37 +00:00
bei.height = static_cast<uint32_t>(alt_chain.size() ? it_prev->second.height + 1 : mainPrevHeight + 1);
2014-05-15 16:40:40 +00:00
bool is_a_checkpoint;
2014-06-20 15:56:33 +00:00
if (!m_checkpoints.check_block(bei.height, id, is_a_checkpoint)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"CHECKPOINT VALIDATION FAILED";
2014-05-15 16:40:40 +00:00
bvc.m_verifivation_failed = true;
return false;
}
// Always check PoW for alternative blocks
m_is_in_checkpoint_zone = false;
2014-03-03 22:07:58 +00:00
difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei);
2015-05-27 12:08:46 +00:00
if (!(current_diff)) { logger(ERROR, BRIGHT_RED) << "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"; return false; }
2014-03-03 22:07:58 +00:00
crypto::hash proof_of_work = null_hash;
2014-08-13 10:51:37 +00:00
if (!m_currency.checkProofOfWork(m_cn_context, bei.bl, current_diff, proof_of_work)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_RED) <<
"Block with id: " << id
2014-05-15 16:40:40 +00:00
<< ENDL << " for alternative chain, have not enough proof of work: " << proof_of_work
2015-05-27 12:08:46 +00:00
<< ENDL << " expected difficulty: " << current_diff;
2014-05-15 16:40:40 +00:00
bvc.m_verifivation_failed = true;
return false;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
if (!prevalidate_miner_transaction(b, bei.height)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_RED) <<
"Block with id: " << Common::podToHex(id) << " (as alternative) have wrong miner transaction.";
2014-03-03 22:07:58 +00:00
bvc.m_verifivation_failed = true;
return false;
}
2014-08-13 10:51:37 +00:00
bei.cumulative_difficulty = alt_chain.size() ? it_prev->second.cumulative_difficulty : m_blocks[mainPrevHeight].cumulative_difficulty;
2014-03-03 22:07:58 +00:00
bei.cumulative_difficulty += current_diff;
#ifdef _DEBUG
auto i_dres = m_alternative_chains.find(id);
2015-05-27 12:08:46 +00:00
if (!(i_dres == m_alternative_chains.end())) { logger(ERROR, BRIGHT_RED) << "insertion of new alternative block returned as it already exist"; return false; }
2014-03-03 22:07:58 +00:00
#endif
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei));
2015-05-27 12:08:46 +00:00
if (!(i_res.second)) { logger(ERROR, BRIGHT_RED) << "insertion of new alternative block returned as it already exist"; return false; }
2014-03-03 22:07:58 +00:00
alt_chain.push_back(i_res.first);
2014-05-15 16:40:40 +00:00
2014-06-20 15:56:33 +00:00
if (is_a_checkpoint) {
2014-03-03 22:07:58 +00:00
//do reorganize!
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_GREEN) <<
"###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 <<
", checkpoint is found in alternative chain on height " << bei.height;
2014-05-15 16:40:40 +00:00
bool r = switch_to_alternative_blockchain(alt_chain, true);
2015-07-15 12:23:00 +00:00
if (r) {
bvc.m_added_to_main_chain = true;
bvc.m_switched_to_alt_chain = true;
} else {
bvc.m_verifivation_failed = true;
}
2014-05-15 16:40:40 +00:00
return r;
2014-06-20 15:56:33 +00:00
} else if (m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain
2014-05-15 16:40:40 +00:00
{
//do reorganize!
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_GREEN) <<
"###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << " with cum_difficulty " << m_blocks.back().cumulative_difficulty
<< ENDL << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty;
2014-05-15 16:40:40 +00:00
bool r = switch_to_alternative_blockchain(alt_chain, false);
2015-07-15 12:23:00 +00:00
if (r) {
bvc.m_added_to_main_chain = true;
bvc.m_switched_to_alt_chain = true;
} else {
bvc.m_verifivation_failed = true;
}
2014-03-03 22:07:58 +00:00
return r;
2014-06-20 15:56:33 +00:00
} else {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_BLUE) <<
"----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height
2014-05-15 16:40:40 +00:00
<< ENDL << "id:\t" << id
<< ENDL << "PoW:\t" << proof_of_work
2015-05-27 12:08:46 +00:00
<< ENDL << "difficulty:\t" << current_diff;
2014-05-15 16:40:40 +00:00
return true;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
} else {
2014-03-03 22:07:58 +00:00
//block orphaned
bvc.m_marked_as_orphaned = true;
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_RED) <<
"Block recognized as orphaned and rejected, id = " << id;
2014-03-03 22:07:58 +00:00
}
return true;
2015-05-27 12:08:46 +00:00
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list<Block>& blocks, std::list<Transaction>& txs) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
if (start_offset >= m_blocks.size())
2014-03-03 22:07:58 +00:00
return false;
2015-05-27 12:08:46 +00:00
for (size_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) {
2014-03-03 22:07:58 +00:00
blocks.push_back(m_blocks[i].bl);
std::list<crypto::hash> missed_ids;
2014-08-13 10:51:37 +00:00
get_transactions(m_blocks[i].bl.txHashes, txs, missed_ids);
2015-05-27 12:08:46 +00:00
if (!(!missed_ids.size())) { logger(ERROR, BRIGHT_RED) << "have missed transactions in own block in main blockchain"; return false; }
2014-03-03 22:07:58 +00:00
}
return true;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list<Block>& blocks) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
if (start_offset >= m_blocks.size()) {
2014-03-03 22:07:58 +00:00
return false;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
2014-06-20 15:56:33 +00:00
for (size_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) {
2014-03-03 22:07:58 +00:00
blocks.push_back(m_blocks[i].bl);
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-03-03 22:07:58 +00:00
rsp.current_blockchain_height = get_current_blockchain_height();
2014-08-13 10:51:37 +00:00
std::list<Block> blocks;
2014-03-03 22:07:58 +00:00
get_blocks(arg.blocks, blocks, rsp.missed_ids);
2014-06-20 15:56:33 +00:00
for (const auto& bl : blocks) {
2014-03-03 22:07:58 +00:00
std::list<crypto::hash> missed_tx_id;
2014-08-13 10:51:37 +00:00
std::list<Transaction> txs;
get_transactions(bl.txHashes, txs, rsp.missed_ids);
2015-05-27 12:08:46 +00:00
if (!(!missed_tx_id.size())) { logger(ERROR, BRIGHT_RED) << "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() << ENDL << "for block id = " << get_block_hash(bl); return false; }
2014-06-20 15:56:33 +00:00
rsp.blocks.push_back(block_complete_entry());
block_complete_entry& e = rsp.blocks.back();
//pack block
e.block = t_serializable_object_to_blob(bl);
//pack transactions
2014-08-13 10:51:37 +00:00
for (Transaction& tx : txs) {
2014-06-20 15:56:33 +00:00
e.txs.push_back(t_serializable_object_to_blob(tx));
}
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
//get another transactions, if need
2014-08-13 10:51:37 +00:00
std::list<Transaction> txs;
2014-03-03 22:07:58 +00:00
get_transactions(arg.txs, txs, rsp.missed_ids);
//pack aside transactions
2014-06-20 15:56:33 +00:00
for (const auto& tx : txs) {
2014-03-03 22:07:58 +00:00
rsp.txs.push_back(t_serializable_object_to_blob(tx));
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
return true;
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage::get_alternative_blocks(std::list<Block>& blocks) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
for (auto& alt_bl : m_alternative_chains) {
2014-03-03 22:07:58 +00:00
blocks.push_back(alt_bl.second.bl);
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
size_t blockchain_storage::get_alternative_blocks_count() {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-03-03 22:07:58 +00:00
return m_alternative_chains.size();
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::add_out_to_get_random_outs(std::vector<std::pair<TransactionIndex, uint16_t>>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-08-13 10:51:37 +00:00
const Transaction& tx = transactionByIndex(amount_outs[i].first).tx;
2015-05-27 12:08:46 +00:00
if (!(tx.vout.size() > amount_outs[i].second)) {
logger(ERROR, BRIGHT_RED) << "internal error: in global outs index, transaction out index="
<< amount_outs[i].second << " more than transaction outputs = " << tx.vout.size() << ", for tx id = " << get_transaction_hash(tx); return false;
}
if (!(tx.vout[amount_outs[i].second].target.type() == typeid(TransactionOutputToKey))) { logger(ERROR, BRIGHT_RED) << "unknown tx out type"; return false; }
2014-03-03 22:07:58 +00:00
//check if transaction is unlocked
2014-08-13 10:51:37 +00:00
if (!is_tx_spendtime_unlocked(tx.unlockTime))
2014-03-03 22:07:58 +00:00
return false;
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
oen.global_amount_index = i;
2014-08-13 10:51:37 +00:00
oen.out_key = boost::get<TransactionOutputToKey>(tx.vout[amount_outs[i].second].target).key;
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
size_t blockchain_storage::find_end_of_allowed_index(const std::vector<std::pair<TransactionIndex, uint16_t>>& amount_outs) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
if (amount_outs.empty()) {
2014-03-03 22:07:58 +00:00
return 0;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
size_t i = amount_outs.size();
2014-06-20 15:56:33 +00:00
do {
2014-03-03 22:07:58 +00:00
--i;
2014-08-13 10:51:37 +00:00
if (amount_outs[i].first.block + m_currency.minedMoneyUnlockWindow() <= get_current_blockchain_height()) {
2014-06-20 15:56:33 +00:00
return i + 1;
}
2014-03-03 22:07:58 +00:00
} while (i != 0);
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return 0;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
for (uint64_t amount : req.amounts) {
2014-03-03 22:07:58 +00:00
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount());
result_outs.amount = amount;
auto it = m_outputs.find(amount);
2014-06-20 15:56:33 +00:00
if (it == m_outputs.end()) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: not outs for amount " << amount << ", wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist";
2014-03-03 22:07:58 +00:00
continue;//actually this is strange situation, wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist
}
2014-06-20 15:56:33 +00:00
std::vector<std::pair<TransactionIndex, uint16_t>>& amount_outs = it->second;
2014-03-03 22:07:58 +00:00
//it is not good idea to use top fresh outs, because it increases possibility of transaction canceling on split
//lets find upper bound of not fresh outs
size_t up_index_limit = find_end_of_allowed_index(amount_outs);
2015-05-27 12:08:46 +00:00
if (!(up_index_limit <= amount_outs.size())) { logger(ERROR, BRIGHT_RED) << "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size(); return false; }
if (up_index_limit > 0) {
ShuffleGenerator<size_t, crypto::random_engine<size_t>> generator(up_index_limit);
for (uint64_t j = 0; j < up_index_limit && result_outs.outs.size() < req.outs_count; ++j) {
add_out_to_get_random_outs(amount_outs, result_outs, amount, generator());
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
}
}
return true;
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
if (!qblock_ids.size() /*|| !req.m_total_height*/) {
logger(ERROR, BRIGHT_RED) <<
"Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection";
2014-03-03 22:07:58 +00:00
return false;
}
//check genesis match
2015-05-27 12:08:46 +00:00
if (qblock_ids.back() != get_block_hash(m_blocks[0].bl)) {
logger(ERROR, BRIGHT_RED) <<
"Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << ENDL << "id: "
2014-03-03 22:07:58 +00:00
<< qblock_ids.back() << ", " << ENDL << "expected: " << get_block_hash(m_blocks[0].bl)
2015-05-27 12:08:46 +00:00
<< "," << ENDL << " dropping connection";
2014-03-03 22:07:58 +00:00
return false;
}
/* Figure out what blocks we should request to get state_normal */
2014-08-13 10:51:37 +00:00
if (m_blockIndex.findSupplement(qblock_ids, starter_offset)) {
return true;
2014-03-03 22:07:58 +00:00
}
2014-08-13 10:51:37 +00:00
//this should NEVER happen, but, dose of paranoia in such cases is not too bad
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Internal error handling connection, can't find split point";
2014-08-13 10:51:37 +00:00
return false;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
uint64_t blockchain_storage::block_difficulty(size_t i) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
if (!(i < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "wrong block index i = " << i << " at blockchain_storage::block_difficulty()"; return false; }
2014-06-20 15:56:33 +00:00
if (i == 0)
2014-03-03 22:07:58 +00:00
return m_blocks[i].cumulative_difficulty;
2014-06-20 15:56:33 +00:00
return m_blocks[i].cumulative_difficulty - m_blocks[i - 1].cumulative_difficulty;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) {
2014-03-03 22:07:58 +00:00
std::stringstream ss;
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
if (start_index >= m_blocks.size()) {
logger(INFO, BRIGHT_WHITE) <<
"Wrong starter index set: " << start_index << ", expected max index " << m_blocks.size() - 1;
2014-03-03 22:07:58 +00:00
return;
}
2015-05-27 12:08:46 +00:00
for (size_t i = start_index; i != m_blocks.size() && i != end_index; i++) {
2014-03-20 11:46:11 +00:00
ss << "height " << i << ", timestamp " << m_blocks[i].bl.timestamp << ", cumul_dif " << m_blocks[i].cumulative_difficulty << ", cumul_size " << m_blocks[i].block_cumulative_size
2014-06-20 15:56:33 +00:00
<< "\nid\t\t" << get_block_hash(m_blocks[i].bl)
2014-08-13 10:51:37 +00:00
<< "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.txHashes.size() << ENDL;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) <<
"Current blockchain:" << ENDL << ss.str();
logger(INFO, BRIGHT_WHITE) <<
"Blockchain printed with log level 1";
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
void blockchain_storage::print_blockchain_index() {
2014-03-03 22:07:58 +00:00
std::stringstream ss;
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-08-13 10:51:37 +00:00
std::list<crypto::hash> blockIds;
m_blockIndex.getBlockIds(0, std::numeric_limits<size_t>::max(), blockIds);
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Current blockchain index:" << ENDL;
2014-08-13 10:51:37 +00:00
size_t height = 0;
for (auto i = blockIds.begin(); i != blockIds.end(); ++i, ++height) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"id\t\t" << *i << " height" << height;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
void blockchain_storage::print_blockchain_outs(const std::string& file) {
2014-03-03 22:07:58 +00:00
std::stringstream ss;
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
for (const outputs_container::value_type& v : m_outputs) {
const std::vector<std::pair<TransactionIndex, uint16_t>>& vals = v.second;
if (!vals.empty()) {
ss << "amount: " << v.first << ENDL;
for (size_t i = 0; i != vals.size(); i++) {
ss << "\t" << get_transaction_hash(transactionByIndex(vals[i].first).tx) << ": " << vals[i].second << ENDL;
}
2014-03-03 22:07:58 +00:00
}
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
if (Common::saveStringToFile(file, ss.str())) {
logger(INFO, BRIGHT_WHITE) <<
"Current outputs index writen to file: " << file;
2014-06-20 15:56:33 +00:00
} else {
2015-05-27 12:08:46 +00:00
logger(WARNING, BRIGHT_YELLOW) <<
"Failed to write current outputs index to file: " << file;
2014-03-03 22:07:58 +00:00
}
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
if (!find_blockchain_supplement(qblock_ids, resp.start_height))
2014-03-03 22:07:58 +00:00
return false;
resp.total_height = get_current_blockchain_height();
size_t count = 0;
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
return m_blockIndex.getBlockIds(resp.start_height, BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT, resp.m_block_ids);
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<std::pair<Block, std::list<Transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
if (!find_blockchain_supplement(qblock_ids, start_height)) {
2014-03-03 22:07:58 +00:00
return false;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
total_height = get_current_blockchain_height();
size_t count = 0;
2014-06-20 15:56:33 +00:00
for (size_t i = start_height; i != m_blocks.size() && count < max_count; i++, count++) {
blocks.resize(blocks.size() + 1);
2014-03-03 22:07:58 +00:00
blocks.back().first = m_blocks[i].bl;
std::list<crypto::hash> mis;
2014-08-13 10:51:37 +00:00
get_transactions(m_blocks[i].bl.txHashes, blocks.back().second, mis);
2015-05-27 12:08:46 +00:00
if (!(!mis.size())) { logger(ERROR, BRIGHT_RED) << "internal error, transaction from block not found"; return false; }
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
bool blockchain_storage::have_block(const crypto::hash& id) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-08-13 10:51:37 +00:00
if (m_blockIndex.hasBlock(id))
2014-03-03 22:07:58 +00:00
return true;
2014-06-20 15:56:33 +00:00
if (m_alternative_chains.count(id))
2014-03-03 22:07:58 +00:00
return true;
return false;
}
2014-06-20 15:56:33 +00:00
size_t blockchain_storage::get_total_transactions() {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
return m_transactionMap.size();
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-06-20 15:56:33 +00:00
auto it = m_transactionMap.find(tx_id);
if (it == m_transactionMap.end()) {
2015-05-27 12:08:46 +00:00
logger(WARNING, YELLOW) << "warning: get_tx_outputs_gindexs failed to find transaction with id = " << tx_id;
2014-03-03 22:07:58 +00:00
return false;
}
2014-08-13 10:51:37 +00:00
const TransactionEntry& tx = transactionByIndex(it->second);
2015-05-27 12:08:46 +00:00
if (!(tx.m_global_output_indexes.size())) { logger(ERROR, BRIGHT_RED) << "internal error: global indexes for transaction " << tx_id << " is empty"; return false; }
2014-06-20 15:56:33 +00:00
indexs.resize(tx.m_global_output_indexes.size());
for (size_t i = 0; i < tx.m_global_output_indexes.size(); ++i) {
indexs[i] = tx.m_global_output_indexes[i];
}
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::check_tx_inputs(const Transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, BlockInfo* tail) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-08-13 10:51:37 +00:00
if (tail)
tail->id = get_tail_id(tail->height);
2014-03-03 22:07:58 +00:00
bool res = check_tx_inputs(tx, &max_used_block_height);
2014-06-20 15:56:33 +00:00
if (!res) return false;
2015-05-27 12:08:46 +00:00
if (!(max_used_block_height < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size(); return false; }
2014-03-03 22:07:58 +00:00
get_block_hash(m_blocks[max_used_block_height].bl, max_used_block_id);
return true;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::have_tx_keyimges_as_spent(const Transaction &tx) {
for (const auto& in : tx.vin) {
if (in.type() == typeid(TransactionInputToKey)) {
if (have_tx_keyimg_as_spent(boost::get<TransactionInputToKey>(in).keyImage)) {
return true;
}
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return false;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::check_tx_inputs(const Transaction& tx, uint64_t* pmax_used_block_height) {
2014-03-03 22:07:58 +00:00
crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height);
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::check_tx_inputs(const Transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) {
size_t inputIndex = 0;
2014-06-20 15:56:33 +00:00
if (pmax_used_block_height) {
2014-03-03 22:07:58 +00:00
*pmax_used_block_height = 0;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
crypto::hash transactionHash = get_transaction_hash(tx);
2014-06-20 15:56:33 +00:00
for (const auto& txin : tx.vin) {
2014-08-13 10:51:37 +00:00
assert(inputIndex < tx.signatures.size());
if (txin.type() == typeid(TransactionInputToKey)) {
const TransactionInputToKey& in_to_key = boost::get<TransactionInputToKey>(txin);
2015-05-27 12:08:46 +00:00
if (!(!in_to_key.keyOffsets.empty())) { logger(ERROR, BRIGHT_RED) << "empty in_to_key.keyOffsets in transaction with id " << get_transaction_hash(tx); return false; }
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
if (have_tx_keyimg_as_spent(in_to_key.keyImage)) {
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) <<
"Key image already spent in blockchain: " << Common::podToHex(in_to_key.keyImage);
2014-08-13 10:51:37 +00:00
return false;
}
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
if (!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[inputIndex], pmax_used_block_height)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Failed to check ring signature for tx " << transactionHash;
2014-08-13 10:51:37 +00:00
return false;
}
2014-03-03 22:07:58 +00:00
2014-08-13 10:51:37 +00:00
++inputIndex;
} else if (txin.type() == typeid(TransactionInputMultisignature)) {
if (!validateInput(::boost::get<TransactionInputMultisignature>(txin), transactionHash, tx_prefix_hash, tx.signatures[inputIndex])) {
return false;
}
++inputIndex;
} else {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Transaction << " << transactionHash << " contains input of unsupported type.";
2014-03-03 22:07:58 +00:00
return false;
}
}
return true;
}
2014-06-20 15:56:33 +00:00
bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) {
2014-08-13 10:51:37 +00:00
if (unlock_time < m_currency.maxBlockHeight()) {
2014-03-03 22:07:58 +00:00
//interpret as block index
2014-08-13 10:51:37 +00:00
if (get_current_blockchain_height() - 1 + m_currency.lockedTxAllowedDeltaBlocks() >= unlock_time)
2014-03-03 22:07:58 +00:00
return true;
else
return false;
2014-06-20 15:56:33 +00:00
} else {
2014-03-03 22:07:58 +00:00
//interpret as time
uint64_t current_time = static_cast<uint64_t>(time(NULL));
2014-08-13 10:51:37 +00:00
if (current_time + m_currency.lockedTxAllowedDeltaSeconds() >= unlock_time)
2014-03-03 22:07:58 +00:00
return true;
else
return false;
}
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
return false;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::check_tx_input(const TransactionInputToKey& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
struct outputs_visitor {
2014-03-03 22:07:58 +00:00
std::vector<const crypto::public_key *>& m_results_collector;
blockchain_storage& m_bch;
2015-05-27 12:08:46 +00:00
LoggerRef logger;
outputs_visitor(std::vector<const crypto::public_key *>& results_collector, blockchain_storage& bch, ILogger& logger) :m_results_collector(results_collector), m_bch(bch), logger(logger, "outputs_visitor") {
}
2015-07-15 12:23:00 +00:00
bool handle_output(const Transaction& tx, const TransactionOutput& out, size_t transactionOutputIndex) {
2014-03-03 22:07:58 +00:00
//check tx unlock time
2014-08-13 10:51:37 +00:00
if (!m_bch.is_tx_spendtime_unlocked(tx.unlockTime)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"One of outputs for one of inputs have wrong tx.unlockTime = " << tx.unlockTime;
2014-03-03 22:07:58 +00:00
return false;
}
2015-05-27 12:08:46 +00:00
if (out.target.type() != typeid(TransactionOutputToKey)) {
logger(INFO, BRIGHT_WHITE) <<
"Output have wrong type id, which=" << out.target.which();
2014-03-03 22:07:58 +00:00
return false;
}
2014-08-13 10:51:37 +00:00
m_results_collector.push_back(&boost::get<TransactionOutputToKey>(out.target).key);
2014-03-03 22:07:58 +00:00
return true;
}
};
//check ring signature
std::vector<const crypto::public_key *> output_keys;
2015-05-27 12:08:46 +00:00
outputs_visitor vi(output_keys, *this, logger.getLogger());
2014-06-20 15:56:33 +00:00
if (!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Failed to get output keys for tx with amount = " << m_currency.formatAmount(txin.amount) <<
" and count indexes " << txin.keyOffsets.size();
2014-03-03 22:07:58 +00:00
return false;
}
2014-08-13 10:51:37 +00:00
if (txin.keyOffsets.size() != output_keys.size()) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.keyOffsets.size() << " returned wrong keys count " << output_keys.size();
2014-03-03 22:07:58 +00:00
return false;
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
if (!(sig.size() == output_keys.size())) { logger(ERROR, BRIGHT_RED) << "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size(); return false; }
2014-06-20 15:56:33 +00:00
if (m_is_in_checkpoint_zone) {
2014-03-03 22:07:58 +00:00
return true;
2014-06-20 15:56:33 +00:00
}
2014-08-13 10:51:37 +00:00
return crypto::check_ring_signature(tx_prefix_hash, txin.keyImage, output_keys, sig.data());
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
uint64_t blockchain_storage::get_adjusted_time() {
2014-03-03 22:07:58 +00:00
//TODO: add collecting median time
return time(NULL);
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::check_block_timestamp_main(const Block& b) {
if (b.timestamp > get_adjusted_time() + m_currency.blockFutureTimeLimit()) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours";
2014-03-03 22:07:58 +00:00
return false;
}
std::vector<uint64_t> timestamps;
2014-08-13 10:51:37 +00:00
size_t offset = m_blocks.size() <= m_currency.timestampCheckWindow() ? 0 : m_blocks.size() - m_currency.timestampCheckWindow();
2014-06-20 15:56:33 +00:00
for (; offset != m_blocks.size(); ++offset) {
2014-03-03 22:07:58 +00:00
timestamps.push_back(m_blocks[offset].bl.timestamp);
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
return check_block_timestamp(std::move(timestamps), b);
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
bool blockchain_storage::check_block_timestamp(std::vector<uint64_t> timestamps, const Block& b) {
if (timestamps.size() < m_currency.timestampCheckWindow()) {
2014-03-03 22:07:58 +00:00
return true;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
2015-07-15 12:23:00 +00:00
uint64_t median_ts = Common::medianValue(timestamps);
2014-03-03 22:07:58 +00:00
2014-06-20 15:56:33 +00:00
if (b.timestamp < median_ts) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp <<
", less than median of last " << m_currency.timestampCheckWindow() << " blocks, " << median_ts;
2014-08-13 10:51:37 +00:00
return false;
}
return true;
}
bool blockchain_storage::checkBlockVersion(const Block& b, const crypto::hash& blockHash) {
uint64_t height = get_block_height(b);
const uint8_t expectedBlockVersion = get_block_major_version_for_height(height);
if (b.majorVersion != expectedBlockVersion) {
2015-05-27 12:08:46 +00:00
logger(TRACE) << "Block " << blockHash << " has wrong major version: " << static_cast<int>(b.majorVersion) <<
", at height " << height << " expected version is " << static_cast<int>(expectedBlockVersion);
2014-08-13 10:51:37 +00:00
return false;
}
return true;
}
bool blockchain_storage::checkParentBlockSize(const Block& b, const crypto::hash& blockHash) {
if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) {
auto serializer = makeParentBlockSerializer(b, false, false);
size_t parentBlockSize;
if (!get_object_blobsize(serializer, parentBlockSize)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Block " << blockHash << ": failed to determine parent block size";
2014-08-13 10:51:37 +00:00
return false;
}
if (parentBlockSize > 2 * 1024) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Block " << blockHash << " contains too big parent block: " << parentBlockSize <<
" bytes, expected no more than " << 2 * 1024 << " bytes";
2014-08-13 10:51:37 +00:00
return false;
}
}
return true;
}
bool blockchain_storage::checkCumulativeBlockSize(const crypto::hash& blockId, size_t cumulativeBlockSize, uint64_t height) {
size_t maxBlockCumulativeSize = m_currency.maxBlockCumulativeSize(height);
if (cumulativeBlockSize > maxBlockCumulativeSize) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Block " << blockId << " is too big: " << cumulativeBlockSize << " bytes, " <<
"exptected no more than " << maxBlockCumulativeSize << " bytes";
2014-03-03 22:07:58 +00:00
return false;
}
return true;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
// Returns true, if cumulativeSize is calculated precisely, else returns false.
bool blockchain_storage::getBlockCumulativeSize(const Block& block, size_t& cumulativeSize) {
std::vector<Transaction> blockTxs;
std::vector<crypto::hash> missedTxs;
get_transactions(block.txHashes, blockTxs, missedTxs, true);
cumulativeSize = get_object_blobsize(block.minerTx);
for (const Transaction& tx : blockTxs) {
cumulativeSize += get_object_blobsize(tx);
}
return missedTxs.empty();
}
2014-08-14 15:41:44 +00:00
// Precondition: m_blockchain_lock is locked.
2014-06-20 15:56:33 +00:00
bool blockchain_storage::update_next_comulative_size_limit() {
2014-08-14 15:41:44 +00:00
uint8_t nextBlockMajorVersion = get_block_major_version_for_height(m_blocks.size());
size_t nextBlockGrantedFullRewardZone = nextBlockMajorVersion == BLOCK_MAJOR_VERSION_1 ?
parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 :
m_currency.blockGrantedFullRewardZone();
2014-06-20 15:56:33 +00:00
std::vector<size_t> sz;
2014-08-13 10:51:37 +00:00
get_last_n_blocks_sizes(sz, m_currency.rewardBlocksWindow());
2014-06-20 15:56:33 +00:00
2015-07-15 12:23:00 +00:00
uint64_t median = Common::medianValue(sz);
2014-08-14 15:41:44 +00:00
if (median <= nextBlockGrantedFullRewardZone) {
median = nextBlockGrantedFullRewardZone;
}
2014-06-20 15:56:33 +00:00
m_current_block_cumul_sz_limit = median * 2;
return true;
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage::add_new_block(const Block& bl_, block_verification_context& bvc) {
2014-06-20 15:56:33 +00:00
//copy block here to let modify block.target
2014-08-13 10:51:37 +00:00
Block bl = bl_;
crypto::hash id;
if (!get_block_hash(bl, id)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Failed to get block hash, possible block has invalid format";
2014-08-13 10:51:37 +00:00
bvc.m_verifivation_failed = true;
return false;
}
bool add_result;
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
{ //to avoid deadlock lets lock tx_pool for whole add/reorganize process
std::lock_guard<decltype(m_tx_pool)> poolLock(m_tx_pool);
std::lock_guard<decltype(m_blockchain_lock)> bcLock(m_blockchain_lock);
if (have_block(id)) {
logger(TRACE) << "block with id = " << id << " already exists";
bvc.m_already_exists = true;
return false;
}
//check that block refers to chain tail
if (!(bl.prevId == get_tail_id())) {
//chain switching or wrong block
bvc.m_added_to_main_chain = false;
add_result = handle_alternative_block(bl, id, bvc);
} else {
add_result = pushBlock(bl, bvc);
}
}
if (add_result && bvc.m_added_to_main_chain) {
m_observerManager.notify(&IBlockchainStorageObserver::blockchainUpdated);
2014-06-20 15:56:33 +00:00
}
return add_result;
2014-06-20 15:56:33 +00:00
}
2014-08-13 10:51:37 +00:00
const blockchain_storage::TransactionEntry& blockchain_storage::transactionByIndex(TransactionIndex index) {
2014-06-20 15:56:33 +00:00
return m_blocks[index.block].transactions[index.transaction];
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage::pushBlock(const Block& blockData, block_verification_context& bvc) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
auto blockProcessingStart = std::chrono::steady_clock::now();
2014-06-20 15:56:33 +00:00
crypto::hash blockHash = get_block_hash(blockData);
2014-08-13 10:51:37 +00:00
if (m_blockIndex.hasBlock(blockHash)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Block " << blockHash << " already exists in blockchain.";
2014-06-20 15:56:33 +00:00
bvc.m_verifivation_failed = true;
2014-03-03 22:07:58 +00:00
return false;
}
2014-08-13 10:51:37 +00:00
if (!checkBlockVersion(blockData, blockHash)) {
bvc.m_verifivation_failed = true;
return false;
}
if (!checkParentBlockSize(blockData, blockHash)) {
bvc.m_verifivation_failed = true;
return false;
}
if (blockData.prevId != get_tail_id()) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Block " << blockHash << " has wrong prevId: " << blockData.prevId << ", expected: " << get_tail_id();
2014-06-20 15:56:33 +00:00
bvc.m_verifivation_failed = true;
return false;
}
if (!check_block_timestamp_main(blockData)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Block " << blockHash << " has invalid timestamp: " << blockData.timestamp;
2014-03-03 22:07:58 +00:00
bvc.m_verifivation_failed = true;
return false;
}
2015-05-27 12:08:46 +00:00
auto targetTimeStart = std::chrono::steady_clock::now();
2014-06-20 15:56:33 +00:00
difficulty_type currentDifficulty = get_difficulty_for_next_block();
2015-05-27 12:08:46 +00:00
auto target_calculating_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - targetTimeStart).count();
if (!(currentDifficulty)) {
logger(ERROR, BRIGHT_RED) << "!!!!!!!!! difficulty overhead !!!!!!!!!";
return false;
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
auto longhashTimeStart = std::chrono::steady_clock::now();
2014-03-03 22:07:58 +00:00
crypto::hash proof_of_work = null_hash;
2014-06-20 15:56:33 +00:00
if (m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())) {
if (!m_checkpoints.check_block(get_current_blockchain_height(), blockHash)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"CHECKPOINT VALIDATION FAILED";
2014-03-03 22:07:58 +00:00
bvc.m_verifivation_failed = true;
return false;
}
2014-06-20 15:56:33 +00:00
} else {
2014-08-13 10:51:37 +00:00
if (!m_currency.checkProofOfWork(m_cn_context, blockData, currentDifficulty, proof_of_work)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Block " << blockHash << ", has too weak proof of work: " << proof_of_work << ", expected difficulty: " << currentDifficulty;
2014-03-03 22:07:58 +00:00
bvc.m_verifivation_failed = true;
return false;
}
}
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
auto longhash_calculating_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - longhashTimeStart).count();
2014-03-03 22:07:58 +00:00
2014-06-20 15:56:33 +00:00
if (!prevalidate_miner_transaction(blockData, m_blocks.size())) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Block " << blockHash << " failed to pass prevalidation";
2014-03-03 22:07:58 +00:00
bvc.m_verifivation_failed = true;
return false;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
crypto::hash minerTransactionHash = get_transaction_hash(blockData.minerTx);
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
BlockEntry block;
2014-06-20 15:56:33 +00:00
block.bl = blockData;
block.transactions.resize(1);
2015-05-27 12:08:46 +00:00
block.transactions[0].tx = blockData.minerTx;
TransactionIndex transactionIndex = {static_cast<uint32_t>(m_blocks.size()), static_cast<uint16_t>(0)};
2014-06-20 15:56:33 +00:00
pushTransaction(block, minerTransactionHash, transactionIndex);
2014-08-13 10:51:37 +00:00
size_t coinbase_blob_size = get_object_blobsize(blockData.minerTx);
2014-03-03 22:07:58 +00:00
size_t cumulative_block_size = coinbase_blob_size;
uint64_t fee_summary = 0;
2014-08-13 10:51:37 +00:00
for (const crypto::hash& tx_id : blockData.txHashes) {
2014-06-20 15:56:33 +00:00
block.transactions.resize(block.transactions.size() + 1);
2014-03-03 22:07:58 +00:00
size_t blob_size = 0;
uint64_t fee = 0;
2014-06-20 15:56:33 +00:00
if (!m_tx_pool.take_tx(tx_id, block.transactions.back().tx, blob_size, fee)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Block " << blockHash << " has at least one unknown transaction: " << tx_id;
2014-03-03 22:07:58 +00:00
bvc.m_verifivation_failed = true;
2015-05-27 12:08:46 +00:00
tx_verification_context tvc = boost::value_initialized<decltype(tvc)>();
2014-06-20 15:56:33 +00:00
block.transactions.pop_back();
popTransactions(block, minerTransactionHash);
2014-03-03 22:07:58 +00:00
return false;
}
2014-06-20 15:56:33 +00:00
if (!check_tx_inputs(block.transactions.back().tx)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Block " << blockHash << " has at least one transaction with wrong inputs: " << tx_id;
2014-03-03 22:07:58 +00:00
bvc.m_verifivation_failed = true;
2015-05-27 12:08:46 +00:00
tx_verification_context tvc = boost::value_initialized<decltype(tvc)>();;
2014-06-20 15:56:33 +00:00
if (!m_tx_pool.add_tx(block.transactions.back().tx, tvc, true)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Cannot move transaction from blockchain to transaction pool.";
2014-06-20 15:56:33 +00:00
}
block.transactions.pop_back();
popTransactions(block, minerTransactionHash);
2014-03-03 22:07:58 +00:00
return false;
}
2014-06-20 15:56:33 +00:00
++transactionIndex.transaction;
pushTransaction(block, tx_id, transactionIndex);
2014-03-03 22:07:58 +00:00
cumulative_block_size += blob_size;
2014-06-20 15:56:33 +00:00
fee_summary += fee;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
if (!checkCumulativeBlockSize(blockHash, cumulative_block_size, m_blocks.size())) {
bvc.m_verifivation_failed = true;
return false;
}
int64_t emissionChange = 0;
uint64_t reward = 0;
uint64_t already_generated_coins = m_blocks.empty() ? 0 : m_blocks.back().already_generated_coins;
if (!validate_miner_transaction(blockData, m_blocks.size(), cumulative_block_size, already_generated_coins, fee_summary, reward, emissionChange)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) << "Block " << blockHash << " has invalid miner transaction";
2014-03-03 22:07:58 +00:00
bvc.m_verifivation_failed = true;
2014-06-20 15:56:33 +00:00
popTransactions(block, minerTransactionHash);
2014-03-03 22:07:58 +00:00
return false;
}
2014-06-20 15:56:33 +00:00
block.height = static_cast<uint32_t>(m_blocks.size());
block.block_cumulative_size = cumulative_block_size;
block.cumulative_difficulty = currentDifficulty;
2014-08-13 10:51:37 +00:00
block.already_generated_coins = already_generated_coins + emissionChange;
2014-06-20 15:56:33 +00:00
if (m_blocks.size() > 0) {
block.cumulative_difficulty += m_blocks.back().cumulative_difficulty;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
pushBlock(block);
2015-05-27 12:08:46 +00:00
auto block_processing_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - blockProcessingStart).count();
logger(DEBUGGING) <<
"+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << blockHash
2014-03-03 22:07:58 +00:00
<< ENDL << "PoW:\t" << proof_of_work
2014-06-20 15:56:33 +00:00
<< ENDL << "HEIGHT " << block.height << ", difficulty:\t" << currentDifficulty
2014-08-13 10:51:37 +00:00
<< ENDL << "block reward: " << m_currency.formatAmount(reward) << ", fee = " << m_currency.formatAmount(fee_summary)
<< ", coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size
2015-05-27 12:08:46 +00:00
<< ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms";
2014-03-03 22:07:58 +00:00
bvc.m_added_to_main_chain = true;
2014-08-13 10:51:37 +00:00
m_upgradeDetector.blockPushed();
2014-08-14 15:41:44 +00:00
update_next_comulative_size_limit();
2014-08-13 10:51:37 +00:00
2014-03-03 22:07:58 +00:00
return true;
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage::pushBlock(BlockEntry& block) {
2014-06-20 15:56:33 +00:00
crypto::hash blockHash = get_block_hash(block.bl);
2014-03-03 22:07:58 +00:00
2014-06-20 15:56:33 +00:00
m_blocks.push_back(block);
2014-08-13 10:51:37 +00:00
m_blockIndex.push(blockHash);
assert(m_blockIndex.size() == m_blocks.size());
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
void blockchain_storage::popBlock(const crypto::hash& blockHash) {
if (m_blocks.empty()) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Attempt to pop block from empty blockchain.";
2014-06-20 15:56:33 +00:00
return;
}
2014-08-13 10:51:37 +00:00
popTransactions(m_blocks.back(), get_transaction_hash(m_blocks.back().bl.minerTx));
2014-06-20 15:56:33 +00:00
m_blocks.pop_back();
2014-08-13 10:51:37 +00:00
m_blockIndex.pop();
assert(m_blockIndex.size() == m_blocks.size());
m_upgradeDetector.blockPopped();
2014-06-20 15:56:33 +00:00
}
2014-08-13 10:51:37 +00:00
bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex) {
2014-06-20 15:56:33 +00:00
auto result = m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex));
if (!result.second) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Duplicate transaction was pushed to blockchain.";
2014-03-03 22:07:58 +00:00
return false;
}
2014-08-13 10:51:37 +00:00
TransactionEntry& transaction = block.transactions[transactionIndex.transaction];
if (!checkMultisignatureInputsDiff(transaction.tx)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Double spending transaction was pushed to blockchain.";
2014-08-13 10:51:37 +00:00
m_transactionMap.erase(transactionHash);
return false;
}
2014-06-20 15:56:33 +00:00
for (size_t i = 0; i < transaction.tx.vin.size(); ++i) {
2014-08-13 10:51:37 +00:00
if (transaction.tx.vin[i].type() == typeid(TransactionInputToKey)) {
auto result = m_spent_keys.insert(::boost::get<TransactionInputToKey>(transaction.tx.vin[i]).keyImage);
2014-06-20 15:56:33 +00:00
if (!result.second) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Double spending transaction was pushed to blockchain.";
2014-06-20 15:56:33 +00:00
for (size_t j = 0; j < i; ++j) {
2014-08-13 10:51:37 +00:00
m_spent_keys.erase(::boost::get<TransactionInputToKey>(transaction.tx.vin[i - 1 - j]).keyImage);
2014-06-20 15:56:33 +00:00
}
m_transactionMap.erase(transactionHash);
return false;
}
}
}
2014-08-13 10:51:37 +00:00
for (const auto& inv : transaction.tx.vin) {
if (inv.type() == typeid(TransactionInputMultisignature)) {
const TransactionInputMultisignature& in = ::boost::get<TransactionInputMultisignature>(inv);
auto& amountOutputs = m_multisignatureOutputs[in.amount];
amountOutputs[in.outputIndex].isUsed = true;
}
}
2014-06-20 15:56:33 +00:00
transaction.m_global_output_indexes.resize(transaction.tx.vout.size());
for (uint16_t output = 0; output < transaction.tx.vout.size(); ++output) {
2014-08-13 10:51:37 +00:00
if (transaction.tx.vout[output].target.type() == typeid(TransactionOutputToKey)) {
auto& amountOutputs = m_outputs[transaction.tx.vout[output].amount];
2015-05-27 12:08:46 +00:00
transaction.m_global_output_indexes[output] = static_cast<uint32_t>(amountOutputs.size());
2014-08-13 10:51:37 +00:00
amountOutputs.push_back(std::make_pair<>(transactionIndex, output));
} else if (transaction.tx.vout[output].target.type() == typeid(TransactionOutputMultisignature)) {
auto& amountOutputs = m_multisignatureOutputs[transaction.tx.vout[output].amount];
2015-05-27 12:08:46 +00:00
transaction.m_global_output_indexes[output] = static_cast<uint32_t>(amountOutputs.size());
MultisignatureOutputUsage outputUsage = {transactionIndex, output, false};
2014-08-13 10:51:37 +00:00
amountOutputs.push_back(outputUsage);
}
2014-06-20 15:56:33 +00:00
}
return true;
}
2014-08-13 10:51:37 +00:00
void blockchain_storage::popTransaction(const Transaction& transaction, const crypto::hash& transactionHash) {
2014-06-20 15:56:33 +00:00
TransactionIndex transactionIndex = m_transactionMap.at(transactionHash);
2014-08-13 10:51:37 +00:00
for (size_t outputIndex = 0; outputIndex < transaction.vout.size(); ++outputIndex) {
const TransactionOutput& output = transaction.vout[transaction.vout.size() - 1 - outputIndex];
if (output.target.type() == typeid(TransactionOutputToKey)) {
auto amountOutputs = m_outputs.find(output.amount);
if (amountOutputs == m_outputs.end()) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Blockchain consistency broken - cannot find specific amount in outputs map.";
2014-08-13 10:51:37 +00:00
continue;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
if (amountOutputs->second.empty()) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Blockchain consistency broken - output array for specific amount is empty.";
2014-08-13 10:51:37 +00:00
continue;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
if (amountOutputs->second.back().first.block != transactionIndex.block || amountOutputs->second.back().first.transaction != transactionIndex.transaction) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Blockchain consistency broken - invalid transaction index.";
2014-08-13 10:51:37 +00:00
continue;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
if (amountOutputs->second.back().second != transaction.vout.size() - 1 - outputIndex) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Blockchain consistency broken - invalid output index.";
2014-08-13 10:51:37 +00:00
continue;
}
amountOutputs->second.pop_back();
if (amountOutputs->second.empty()) {
m_outputs.erase(amountOutputs);
}
} else if (output.target.type() == typeid(TransactionOutputMultisignature)) {
auto amountOutputs = m_multisignatureOutputs.find(output.amount);
if (amountOutputs == m_multisignatureOutputs.end()) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Blockchain consistency broken - cannot find specific amount in outputs map.";
2014-08-13 10:51:37 +00:00
continue;
}
if (amountOutputs->second.empty()) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Blockchain consistency broken - output array for specific amount is empty.";
2014-08-13 10:51:37 +00:00
continue;
}
if (amountOutputs->second.back().isUsed) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Blockchain consistency broken - attempting to remove used output.";
2014-08-13 10:51:37 +00:00
continue;
}
if (amountOutputs->second.back().transactionIndex.block != transactionIndex.block || amountOutputs->second.back().transactionIndex.transaction != transactionIndex.transaction) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Blockchain consistency broken - invalid transaction index.";
2014-08-13 10:51:37 +00:00
continue;
}
if (amountOutputs->second.back().outputIndex != transaction.vout.size() - 1 - outputIndex) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Blockchain consistency broken - invalid output index.";
2014-08-13 10:51:37 +00:00
continue;
}
2014-06-20 15:56:33 +00:00
2014-08-13 10:51:37 +00:00
amountOutputs->second.pop_back();
if (amountOutputs->second.empty()) {
m_multisignatureOutputs.erase(amountOutputs);
}
2014-06-20 15:56:33 +00:00
}
}
for (auto& input : transaction.vin) {
2014-08-13 10:51:37 +00:00
if (input.type() == typeid(TransactionInputToKey)) {
size_t count = m_spent_keys.erase(::boost::get<TransactionInputToKey>(input).keyImage);
2014-06-20 15:56:33 +00:00
if (count != 1) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Blockchain consistency broken - cannot find spent key.";
2014-06-20 15:56:33 +00:00
}
2014-08-13 10:51:37 +00:00
} else if (input.type() == typeid(TransactionInputMultisignature)) {
const TransactionInputMultisignature& in = ::boost::get<TransactionInputMultisignature>(input);
auto& amountOutputs = m_multisignatureOutputs[in.amount];
if (!amountOutputs[in.outputIndex].isUsed) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Blockchain consistency broken - multisignature output not marked as used.";
2014-08-13 10:51:37 +00:00
}
amountOutputs[in.outputIndex].isUsed = false;
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
size_t count = m_transactionMap.erase(transactionHash);
if (count != 1) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Blockchain consistency broken - cannot find transaction by hash.";
2014-06-20 15:56:33 +00:00
}
}
2014-08-13 10:51:37 +00:00
void blockchain_storage::popTransactions(const BlockEntry& block, const crypto::hash& minerTransactionHash) {
2014-06-20 15:56:33 +00:00
for (size_t i = 0; i < block.transactions.size() - 1; ++i) {
2014-08-13 10:51:37 +00:00
popTransaction(block.transactions[block.transactions.size() - 1 - i].tx, block.bl.txHashes[block.transactions.size() - 2 - i]);
2015-05-27 12:08:46 +00:00
tx_verification_context tvc = boost::value_initialized<decltype(tvc)>();
2014-06-20 15:56:33 +00:00
if (!m_tx_pool.add_tx(block.transactions[block.transactions.size() - 1 - i].tx, tvc, true)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) <<
"Cannot move transaction from blockchain to transaction pool.";
2014-06-20 15:56:33 +00:00
}
}
2014-08-13 10:51:37 +00:00
popTransaction(block.bl.minerTx, minerTransactionHash);
}
bool blockchain_storage::validateInput(const TransactionInputMultisignature& input, const crypto::hash& transactionHash, const crypto::hash& transactionPrefixHash, const std::vector<crypto::signature>& transactionSignatures) {
assert(input.signatures == transactionSignatures.size());
MultisignatureOutputsContainer::const_iterator amountOutputs = m_multisignatureOutputs.find(input.amount);
if (amountOutputs == m_multisignatureOutputs.end()) {
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) <<
"Transaction << " << transactionHash << " contains multisignature input with invalid amount.";
2014-08-13 10:51:37 +00:00
return false;
}
if (input.outputIndex >= amountOutputs->second.size()) {
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) <<
"Transaction << " << transactionHash << " contains multisignature input with invalid outputIndex.";
2014-08-13 10:51:37 +00:00
return false;
}
const MultisignatureOutputUsage& outputIndex = amountOutputs->second[input.outputIndex];
if (outputIndex.isUsed) {
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) <<
"Transaction << " << transactionHash << " contains double spending multisignature input.";
2014-08-13 10:51:37 +00:00
return false;
}
const Transaction& outputTransaction = m_blocks[outputIndex.transactionIndex.block].transactions[outputIndex.transactionIndex.transaction].tx;
if (!is_tx_spendtime_unlocked(outputTransaction.unlockTime)) {
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) <<
"Transaction << " << transactionHash << " contains multisignature input which points to a locked transaction.";
2014-08-13 10:51:37 +00:00
return false;
}
assert(outputTransaction.vout[outputIndex.outputIndex].amount == input.amount);
assert(outputTransaction.vout[outputIndex.outputIndex].target.type() == typeid(TransactionOutputMultisignature));
const TransactionOutputMultisignature& output = ::boost::get<TransactionOutputMultisignature>(outputTransaction.vout[outputIndex.outputIndex].target);
if (input.signatures != output.requiredSignatures) {
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) <<
"Transaction << " << transactionHash << " contains multisignature input with invalid signature count.";
2014-08-13 10:51:37 +00:00
return false;
}
std::size_t inputSignatureIndex = 0;
std::size_t outputKeyIndex = 0;
while (inputSignatureIndex < input.signatures) {
if (outputKeyIndex == output.keys.size()) {
2015-05-27 12:08:46 +00:00
logger(DEBUGGING) <<
"Transaction << " << transactionHash << " contains multisignature input with invalid signatures.";
2014-08-13 10:51:37 +00:00
return false;
}
if (crypto::check_signature(transactionPrefixHash, output.keys[outputKeyIndex], transactionSignatures[inputSignatureIndex])) {
++inputSignatureIndex;
}
++outputKeyIndex;
}
return true;
}
bool blockchain_storage::getLowerBound(uint64_t timestamp, uint64_t startOffset, uint64_t& height) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-08-13 10:51:37 +00:00
if (startOffset >= m_blocks.size()) {
return false;
}
auto bound = std::lower_bound(m_blocks.begin() + startOffset, m_blocks.end(), timestamp - m_currency.blockFutureTimeLimit(),
[](const BlockEntry& b, uint64_t timestamp) { return b.bl.timestamp < timestamp; });
if (bound == m_blocks.end()) {
return false;
}
height = std::distance(m_blocks.begin(), bound);
return true;
}
bool blockchain_storage::getBlockIds(uint64_t startHeight, size_t maxCount, std::list<crypto::hash>& items) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2014-08-13 10:51:37 +00:00
return m_blockIndex.getBlockIds(startHeight, maxCount, items);
2014-06-20 15:56:33 +00:00
}
2015-05-27 12:08:46 +00:00
2015-07-15 12:23:00 +00:00
bool blockchain_storage::getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
auto it = m_transactionMap.find(txId);
if(it == m_transactionMap.end()) {
return false;
} else {
blockHeight = m_blocks[it->second.block].height;
blockId = get_block_id_by_height(blockHeight);
return true;
}
}
bool blockchain_storage::getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
// try to find block in main chain
uint64_t height = 0;
if (m_blockIndex.getBlockHeight(hash, height)) {
generatedCoins = m_blocks[height].already_generated_coins;
return true;
}
// try to find block in alternative chain
auto blockByHashIterator = m_alternative_chains.find(hash);
if (blockByHashIterator != m_alternative_chains.end()) {
generatedCoins = blockByHashIterator->second.already_generated_coins;
return true;
}
logger(DEBUGGING) << "Can't find block with hash " << hash << " to get already generated coins.";
return false;
}
bool blockchain_storage::getBlockSize(const crypto::hash& hash, size_t& size) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
// try to find block in main chain
uint64_t height = 0;
if (m_blockIndex.getBlockHeight(hash, height)) {
size = m_blocks[height].block_cumulative_size;
return true;
}
// try to find block in alternative chain
auto blockByHashIterator = m_alternative_chains.find(hash);
if (blockByHashIterator != m_alternative_chains.end()) {
size = blockByHashIterator->second.block_cumulative_size;
return true;
}
logger(DEBUGGING) << "Can't find block with hash " << hash << " to get block size.";
return false;
}
bool blockchain_storage::getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair<crypto::hash, size_t>& outputReference) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
MultisignatureOutputsContainer::const_iterator amountIter = m_multisignatureOutputs.find(txInMultisig.amount);
if (amountIter == m_multisignatureOutputs.end()) {
logger(DEBUGGING) << "Transaction contains multisignature input with invalid amount.";
return false;
}
if (amountIter->second.size() <= txInMultisig.outputIndex) {
logger(DEBUGGING) << "Transaction contains multisignature input with invalid outputIndex.";
return false;
}
const MultisignatureOutputUsage& outputIndex = amountIter->second[txInMultisig.outputIndex];
const Transaction& outputTransaction = m_blocks[outputIndex.transactionIndex.block].transactions[outputIndex.transactionIndex.transaction].tx;
outputReference.first = get_transaction_hash(outputTransaction);
outputReference.second = outputIndex.outputIndex;
return true;
}
2015-05-27 12:08:46 +00:00
}