danicoin/src/CryptoNoteCore/Blockchain.cpp

2270 lines
83 KiB
C++
Raw Normal View History

// Copyright (c) 2011-2016 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2014-03-03 22:07:58 +00:00
2015-07-30 15:22:07 +00:00
#include "Blockchain.h"
2014-06-20 15:56:33 +00:00
2014-03-03 22:07:58 +00:00
#include <algorithm>
#include <cstdio>
2015-05-27 12:08:46 +00:00
#include <boost/foreach.hpp>
2015-07-15 12:23:00 +00:00
#include "Common/Math.h"
2015-05-27 12:08:46 +00:00
#include "Common/ShuffleGenerator.h"
2015-07-30 15:22:07 +00:00
#include "Common/StdInputStream.h"
#include "Common/StdOutputStream.h"
#include "Rpc/CoreRpcServerCommandsDefinitions.h"
#include "Serialization/BinarySerializationTools.h"
#include "CryptoNoteTools.h"
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
using namespace Logging;
2015-07-30 15:22:07 +00:00
using namespace Common;
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 {
2015-07-30 15:22:07 +00:00
bool operator<(const Crypto::Hash& hash1, const Crypto::Hash& hash2) {
return memcmp(&hash1, &hash2, Crypto::HASH_SIZE) < 0;
2015-05-27 12:08:46 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool operator<(const Crypto::KeyImage& keyImage1, const Crypto::KeyImage& keyImage2) {
2015-05-27 12:08:46 +00:00
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
2015-07-30 15:22:07 +00:00
#define CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER 1
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
namespace CryptoNote {
class BlockCacheSerializer;
2015-07-30 15:22:07 +00:00
class BlockchainIndicesSerializer;
}
namespace CryptoNote {
template<typename K, typename V, typename Hash>
bool serialize(google::sparse_hash_map<K, V, Hash>& value, Common::StringView name, CryptoNote::ISerializer& serializer) {
return serializeMap(value, name, serializer, [&value](size_t size) { value.resize(size); });
}
template<typename K, typename Hash>
bool serialize(google::sparse_hash_set<K, Hash>& value, Common::StringView name, CryptoNote::ISerializer& serializer) {
size_t size = value.size();
if (!serializer.beginArray(size, name)) {
return false;
}
if (serializer.type() == ISerializer::OUTPUT) {
for (auto& key : value) {
serializer(const_cast<K&>(key), "");
}
} else {
value.resize(size);
while (size--) {
K key;
serializer(key, "");
value.insert(key);
}
}
serializer.endArray();
return true;
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
// custom serialization to speedup cache loading
bool serialize(std::vector<std::pair<Blockchain::TransactionIndex, uint16_t>>& value, Common::StringView name, CryptoNote::ISerializer& s) {
const size_t elementSize = sizeof(std::pair<Blockchain::TransactionIndex, uint16_t>);
size_t size = value.size() * elementSize;
if (!s.beginArray(size, name)) {
return false;
}
if (s.type() == CryptoNote::ISerializer::INPUT) {
if (size % elementSize != 0) {
throw std::runtime_error("Invalid vector size");
}
value.resize(size / elementSize);
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
if (size) {
s.binary(value.data(), size, "");
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
s.endArray();
return true;
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
void serialize(Blockchain::TransactionIndex& value, ISerializer& s) {
s(value.block, "block");
s(value.transaction, "tx");
2014-08-13 10:51:37 +00:00
}
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:
2015-07-30 15:22:07 +00:00
BlockCacheSerializer(Blockchain& bs, const Crypto::Hash lastBlockHash, ILogger& logger) :
2015-05-27 12:08:46 +00:00
m_bs(bs), m_lastBlockHash(lastBlockHash), m_loaded(false), logger(logger, "BlockCacheSerializer") {
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
void load(const std::string& filename) {
try {
std::ifstream stdStream(filename, std::ios::binary);
if (!stdStream) {
return;
}
StdInputStream stream(stdStream);
BinaryInputStreamSerializer s(stream);
CryptoNote::serialize(*this, s);
} catch (std::exception& e) {
logger(WARNING) << "loading failed: " << e.what();
}
}
bool save(const std::string& filename) {
try {
std::ofstream file(filename, std::ios::binary);
if (!file) {
return false;
}
StdOutputStream stream(file);
BinaryOutputStreamSerializer s(stream);
CryptoNote::serialize(*this, s);
} catch (std::exception&) {
return false;
}
return true;
}
void serialize(ISerializer& s) {
auto start = std::chrono::steady_clock::now();
uint8_t version = CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER;
s(version, "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;
2015-07-30 15:22:07 +00:00
if (s.type() == ISerializer::INPUT) {
2015-05-27 12:08:46 +00:00
operation = "- loading ";
2015-07-30 15:22:07 +00:00
Crypto::Hash blockHash;
s(blockHash, "last_block");
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 ";
2015-07-30 15:22:07 +00:00
s(m_lastBlockHash, "last_block");
2015-05-27 12:08:46 +00:00
}
logger(INFO) << operation << "block index...";
2015-07-30 15:22:07 +00:00
s(m_bs.m_blockIndex, "block_index");
2015-05-27 12:08:46 +00:00
logger(INFO) << operation << "transaction map...";
2015-07-30 15:22:07 +00:00
s(m_bs.m_transactionMap, "transactions");
2015-07-30 15:22:07 +00:00
logger(INFO) << operation << "spent keys...";
s(m_bs.m_spent_keys, "spent_keys");
2015-05-27 12:08:46 +00:00
logger(INFO) << operation << "outputs...";
2015-07-30 15:22:07 +00:00
s(m_bs.m_outputs, "outputs");
2015-05-27 12:08:46 +00:00
logger(INFO) << operation << "multi-signature outputs...";
2015-07-30 15:22:07 +00:00
s(m_bs.m_multisignatureOutputs, "multisig_outputs");
auto dur = std::chrono::steady_clock::now() - start;
logger(INFO) << "Serialization time: " << std::chrono::duration_cast<std::chrono::milliseconds>(dur).count() << "ms";
m_loaded = true;
}
bool loaded() const {
return m_loaded;
}
private:
LoggerRef logger;
bool m_loaded;
Blockchain& m_bs;
Crypto::Hash m_lastBlockHash;
};
class BlockchainIndicesSerializer {
public:
BlockchainIndicesSerializer(Blockchain& bs, const Crypto::Hash lastBlockHash, ILogger& logger) :
m_bs(bs), m_lastBlockHash(lastBlockHash), m_loaded(false), logger(logger, "BlockchainIndicesSerializer") {
}
void serialize(ISerializer& s) {
uint8_t version = CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER;
KV_MEMBER(version);
// ignore old versions, do rebuild
if (version != CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER)
return;
std::string operation;
if (s.type() == ISerializer::INPUT) {
operation = "- loading ";
Crypto::Hash blockHash;
s(blockHash, "blockHash");
if (blockHash != m_lastBlockHash) {
return;
}
} else {
operation = "- saving ";
s(m_lastBlockHash, "blockHash");
}
logger(INFO) << operation << "paymentID index...";
s(m_bs.m_paymentIdIndex, "paymentIdIndex");
logger(INFO) << operation << "timestamp index...";
s(m_bs.m_timestampIndex, "timestampIndex");
logger(INFO) << operation << "generated transactions index...";
s(m_bs.m_generatedTransactionsIndex, "generatedTransactionsIndex");
m_loaded = true;
}
template<class Archive> void serialize(Archive& ar, unsigned int version) {
// ignore old versions, do rebuild
if (version < CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER)
return;
std::string operation;
if (Archive::is_loading::value) {
operation = "- loading ";
Crypto::Hash blockHash;
ar & blockHash;
if (blockHash != m_lastBlockHash) {
return;
}
} else {
operation = "- saving ";
ar & m_lastBlockHash;
}
logger(INFO) << operation << "paymentID index...";
ar & m_bs.m_paymentIdIndex;
logger(INFO) << operation << "timestamp index...";
ar & m_bs.m_timestampIndex;
logger(INFO) << operation << "generated transactions index...";
ar & m_bs.m_generatedTransactionsIndex;
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;
2015-07-30 15:22:07 +00:00
Blockchain& m_bs;
Crypto::Hash m_lastBlockHash;
2015-05-27 12:08:46 +00:00
};
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
Blockchain::Blockchain(const Currency& currency, tx_memory_pool& tx_pool, ILogger& logger) :
logger(logger, "Blockchain"),
2015-05-27 12:08:46 +00:00
m_currency(currency),
m_tx_pool(tx_pool),
m_current_block_cumul_sz_limit(0),
m_is_in_checkpoint_zone(false),
m_checkpoints(logger) {
2014-08-13 10:51:37 +00:00
m_outputs.set_deleted_key(0);
2015-07-30 15:22:07 +00:00
Crypto::KeyImage nullImage = boost::value_initialized<decltype(nullImage)>();
2014-08-13 10:51:37 +00:00
m_spent_keys.set_deleted_key(nullImage);
}
2015-07-30 15:22:07 +00:00
bool Blockchain::addObserver(IBlockchainStorageObserver* observer) {
return m_observerManager.add(observer);
}
2015-07-30 15:22:07 +00:00
bool Blockchain::removeObserver(IBlockchainStorageObserver* observer) {
return m_observerManager.remove(observer);
}
2015-07-30 15:22:07 +00:00
bool Blockchain::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) {
return checkTransactionInputs(tx, maxUsedBlock.height, maxUsedBlock.id);
2014-08-13 10:51:37 +00:00
}
2015-07-30 15:22:07 +00:00
bool Blockchain::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
2015-07-30 15:22:07 +00:00
if (!lastFailed.empty() && getCurrentBlockchainHeight() > lastFailed.height && getBlockIdByHeight(lastFailed.height) == lastFailed.id) {
2014-08-13 10:51:37 +00:00
return false; //we already sure that this tx is broken for this height
}
2015-05-27 12:08:46 +00:00
2015-07-30 15:22:07 +00:00
if (!checkTransactionInputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) {
2014-08-13 10:51:37 +00:00
lastFailed = tail;
return false;
}
2015-05-27 12:08:46 +00:00
} else {
2015-07-30 15:22:07 +00:00
if (maxUsedBlock.height >= getCurrentBlockchainHeight()) {
2014-08-13 10:51:37 +00:00
return false;
}
2015-07-30 15:22:07 +00:00
if (getBlockIdByHeight(maxUsedBlock.height) != maxUsedBlock.id) {
2014-08-13 10:51:37 +00:00
//if we already failed on this height and id, skip actual ring signature check
2015-07-30 15:22:07 +00:00
if (lastFailed.id == getBlockIdByHeight(lastFailed.height)) {
2014-08-13 10:51:37 +00:00
return false;
}
//check ring signature again, it is possible (with very small chance) that this transaction become again valid
2015-07-30 15:22:07 +00:00
if (!checkTransactionInputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) {
2014-08-13 10:51:37 +00:00
lastFailed = tail;
return false;
}
}
}
return true;
}
2015-07-30 15:22:07 +00:00
bool Blockchain::haveSpentKeyImages(const CryptoNote::Transaction& tx) {
return this->haveTransactionKeyImagesAsSpent(tx);
2014-08-13 10:51:37 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
/**
* \pre m_blockchain_lock is locked
*/
bool Blockchain::checkTransactionSize(size_t blobSize) {
if (blobSize > getCurrentCumulativeBlocksizeLimit() - m_currency.minerTxBlobReservedSize()) {
logger(ERROR) << "transaction is too big " << blobSize << ", maximum allowed size is " <<
(getCurrentCumulativeBlocksizeLimit() - m_currency.minerTxBlobReservedSize());
return false;
}
return true;
}
bool Blockchain::haveTransaction(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
2015-07-30 15:22:07 +00:00
bool Blockchain::have_tx_keyimg_as_spent(const Crypto::KeyImage &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();
}
2015-07-30 15:22:07 +00:00
uint32_t Blockchain::getCurrentBlockchainHeight() {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2015-07-30 15:22:07 +00:00
return static_cast<uint32_t>(m_blocks.size());
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain::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);
2015-07-30 15:22:07 +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());
2015-07-30 15:22:07 +00:00
loader.load(appendPath(config_folder, m_currency.blocksCacheFileName()));
2015-05-27 12:08:46 +00:00
if (!loader.loaded()) {
logger(WARNING, BRIGHT_YELLOW) << "No actual blockchain cache found, rebuilding internal structures...";
2015-07-30 15:22:07 +00:00
rebuildCache();
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
loadBlockchainIndices();
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.";
2015-07-30 15:22:07 +00:00
block_verification_context bvc = boost::value_initialized<block_verification_context>();
pushBlock(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 {
2015-07-30 15:22:07 +00:00
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
}
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)
2015-07-30 15:22:07 +00:00
<< " time ago, current difficulty: " << getDifficultyForNextBlock();
2014-03-03 22:07:58 +00:00
return true;
}
2014-03-20 11:46:11 +00:00
2015-07-30 15:22:07 +00:00
void Blockchain::rebuildCache() {
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 = getObjectHash(transaction.tx);
TransactionIndex transactionIndex = { b, t };
m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex));
// process inputs
for (auto& i : transaction.tx.inputs) {
if (i.type() == typeid(KeyInput)) {
m_spent_keys.insert(::boost::get<KeyInput>(i).keyImage);
} else if (i.type() == typeid(MultisignatureInput)) {
auto out = ::boost::get<MultisignatureInput>(i);
m_multisignatureOutputs[out.amount][out.outputIndex].isUsed = true;
}
}
// process outputs
for (uint16_t o = 0; o < transaction.tx.outputs.size(); ++o) {
const auto& out = transaction.tx.outputs[o];
if (out.target.type() == typeid(KeyOutput)) {
m_outputs[out.amount].push_back(std::make_pair<>(transactionIndex, o));
} else if (out.target.type() == typeid(MultisignatureOutput)) {
MultisignatureOutputUsage usage = { transactionIndex, o, false };
m_multisignatureOutputs[out.amount].push_back(usage);
}
}
}
}
std::chrono::duration<double> duration = std::chrono::steady_clock::now() - timePoint;
logger(INFO, BRIGHT_WHITE) << "Rebuilding internal structures took: " << duration.count();
}
bool Blockchain::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...";
2015-07-30 15:22:07 +00:00
BlockCacheSerializer ser(*this, getTailId(), logger.getLogger());
if (!ser.save(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
2015-07-30 15:22:07 +00:00
bool Blockchain::deinit() {
2014-08-13 10:51:37 +00:00
storeCache();
2015-07-30 15:22:07 +00:00
storeBlockchainIndices();
assert(m_messageQueueList.empty());
2014-08-13 10:51:37 +00:00
return true;
2014-03-03 22:07:58 +00:00
}
2015-07-30 15:22:07 +00:00
bool Blockchain::resetAndSetGenesisBlock(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();
2015-07-30 15:22:07 +00:00
m_paymentIdIndex.clear();
m_timestampIndex.clear();
m_generatedTransactionsIndex.clear();
m_orthanBlocksIndex.clear();
2014-03-03 22:07:58 +00:00
block_verification_context bvc = boost::value_initialized<block_verification_context>();
2015-07-30 15:22:07 +00:00
addNewBlock(b, bvc);
2014-03-03 22:07:58 +00:00
return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
}
2015-07-30 15:22:07 +00:00
Crypto::Hash Blockchain::getTailId(uint32_t& height) {
assert(!m_blocks.empty());
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2015-07-30 15:22:07 +00:00
height = getCurrentBlockchainHeight() - 1;
return getTailId();
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
Crypto::Hash Blockchain::getTailId() {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2015-07-30 15:22:07 +00:00
return m_blocks.empty() ? NULL_HASH : m_blockIndex.getTailId();
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
std::vector<Crypto::Hash> Blockchain::buildSparseChain() {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
assert(m_blockIndex.size() != 0);
return doBuildSparseChain(m_blockIndex.getTailId());
}
2015-05-27 12:08:46 +00:00
2015-07-30 15:22:07 +00:00
std::vector<Crypto::Hash> Blockchain::buildSparseChain(const Crypto::Hash& startBlockId) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
assert(haveBlock(startBlockId));
return doBuildSparseChain(startBlockId);
}
2015-07-30 15:22:07 +00:00
std::vector<Crypto::Hash> Blockchain::doBuildSparseChain(const Crypto::Hash& startBlockId) const {
assert(m_blockIndex.size() != 0);
2015-07-15 12:23:00 +00:00
2015-07-30 15:22:07 +00:00
std::vector<Crypto::Hash> sparseChain;
2015-07-15 12:23:00 +00:00
2015-07-30 15:22:07 +00:00
if (m_blockIndex.hasBlock(startBlockId)) {
sparseChain = m_blockIndex.buildSparseChain(startBlockId);
} else {
assert(m_alternative_chains.count(startBlockId) > 0);
2015-07-15 12:23:00 +00:00
2015-07-30 15:22:07 +00:00
std::vector<Crypto::Hash> alternativeChain;
Crypto::Hash blockchainAncestor;
for (auto it = m_alternative_chains.find(startBlockId); it != m_alternative_chains.end(); it = m_alternative_chains.find(blockchainAncestor)) {
alternativeChain.emplace_back(it->first);
blockchainAncestor = it->second.bl.previousBlockHash;
}
2015-07-30 15:22:07 +00:00
for (size_t i = 1; i <= alternativeChain.size(); i *= 2) {
sparseChain.emplace_back(alternativeChain[i - 1]);
}
2015-07-30 15:22:07 +00:00
assert(!sparseChain.empty());
assert(m_blockIndex.hasBlock(blockchainAncestor));
std::vector<Crypto::Hash> sparseMainChain = m_blockIndex.buildSparseChain(blockchainAncestor);
sparseChain.reserve(sparseChain.size() + sparseMainChain.size());
std::copy(sparseMainChain.begin(), sparseMainChain.end(), std::back_inserter(sparseChain));
}
return sparseChain;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
Crypto::Hash Blockchain::getBlockIdByHeight(uint32_t height) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2015-07-30 15:22:07 +00:00
assert(height < m_blockIndex.size());
2014-08-13 10:51:37 +00:00
return m_blockIndex.getBlockId(height);
2014-03-03 22:07:58 +00:00
}
2015-07-30 15:22:07 +00:00
bool Blockchain::getBlockByHash(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
2015-07-30 15:22:07 +00:00
uint32_t height = 0;
2014-08-13 10:51:37 +00:00
if (m_blockIndex.getBlockHeight(blockHash, height)) {
b = m_blocks[height].bl;
2014-03-03 22:07:58 +00:00
return true;
}
2015-07-30 15:22:07 +00:00
logger(WARNING) << blockHash;
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;
}
2015-07-30 15:22:07 +00:00
bool Blockchain::getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight) {
std::lock_guard<decltype(m_blockchain_lock)> lock(m_blockchain_lock);
return m_blockIndex.getBlockHeight(blockId, blockHeight);
}
difficulty_type Blockchain::getDifficultyForNextBlock() {
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);
}
2015-07-30 15:22:07 +00:00
uint64_t Blockchain::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;
}
}
2015-07-30 15:22:07 +00:00
bool Blockchain::rollback_blockchain_switching(std::list<Block> &original_chain, size_t rollback_height) {
2015-05-27 12:08:46 +00:00
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
2015-07-30 15:22:07 +00:00
for (auto &bl : original_chain) {
2015-05-27 12:08:46 +00:00
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
2015-07-30 15:22:07 +00:00
bool Blockchain::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);
2015-07-30 15:22:07 +00:00
m_orthanBlocksIndex.remove(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);
2015-07-30 15:22:07 +00:00
m_orthanBlocksIndex.remove((*alt_ch_to_orph_iter)->second.bl);
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>();
2015-07-30 15:22:07 +00:00
bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc, false);
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
}
}
2015-07-30 15:22:07 +00:00
std::vector<Crypto::Hash> blocksFromCommonRoot;
blocksFromCommonRoot.reserve(alt_chain.size() + 1);
blocksFromCommonRoot.push_back(alt_chain.front()->second.bl.previousBlockHash);
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) {
2015-07-30 15:22:07 +00:00
blocksFromCommonRoot.push_back(get_block_hash(ch_ent->second.bl));
m_orthanBlocksIndex.remove(ch_ent->second.bl);
2014-03-03 22:07:58 +00:00
m_alternative_chains.erase(ch_ent);
}
sendMessage(BlockchainMessage(ChainSwitchMessage(std::move(blocksFromCommonRoot))));
2015-07-30 15:22:07 +00:00
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
2015-07-30 15:22:07 +00:00
difficulty_type Blockchain::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
2015-07-30 15:22:07 +00:00
bool Blockchain::prevalidate_miner_transaction(const Block& b, uint32_t height) {
2015-05-27 12:08:46 +00:00
2015-07-30 15:22:07 +00:00
if (!(b.baseTransaction.inputs.size() == 1)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED)
<< "coinbase transaction in the block has no inputs";
return false;
}
2015-07-30 15:22:07 +00:00
if (!(b.baseTransaction.inputs[0].type() == typeid(BaseInput))) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED)
<< "coinbase transaction in the block has the wrong type";
return false;
}
2015-07-30 15:22:07 +00:00
if (boost::get<BaseInput>(b.baseTransaction.inputs[0]).blockIndex != height) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_RED) << "The miner transaction in block has invalid height: " <<
2015-07-30 15:22:07 +00:00
boost::get<BaseInput>(b.baseTransaction.inputs[0]).blockIndex << ", expected: " << height;
2014-03-03 22:07:58 +00:00
return false;
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
if (!(b.baseTransaction.unlockTime == height + m_currency.minedMoneyUnlockWindow())) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED)
<< "coinbase transaction transaction have wrong unlock time="
2015-07-30 15:22:07 +00:00
<< b.baseTransaction.unlockTime << ", expected "
2015-05-27 12:08:46 +00:00
<< height + m_currency.minedMoneyUnlockWindow();
return false;
}
2014-03-03 22:07:58 +00:00
2015-07-30 15:22:07 +00:00
if (!check_outs_overflow(b.baseTransaction)) {
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
2015-07-30 15:22:07 +00:00
bool Blockchain::validate_miner_transaction(const Block& b, uint32_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;
2015-07-30 15:22:07 +00:00
for (auto& o : b.baseTransaction.outputs) {
2014-08-13 10:51:37 +00:00
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
if (!m_currency.getBlockReward(blocksSizeMedian, cumulativeBlockSize, alreadyGeneratedCoins, fee, 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
2015-07-30 15:22:07 +00:00
bool Blockchain::getBackwardBlocksSize(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
2015-07-30 15:22:07 +00:00
bool Blockchain::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
}
2015-07-30 15:22:07 +00:00
return getBackwardBlocksSize(m_blocks.size() - 1, sz, count);
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
uint64_t Blockchain::getCurrentCumulativeBlocksizeLimit() {
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-07-30 15:22:07 +00:00
bool Blockchain::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
2015-07-30 15:22:07 +00:00
bool Blockchain::handle_alternative_block(const Block& b, const Crypto::Hash& id, block_verification_context& bvc, bool sendNewAlternativeBlockMessage) {
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-07-30 15:22:07 +00:00
auto 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
2015-07-30 15:22:07 +00:00
if (!m_checkpoints.is_alternative_block_allowed(getCurrentBlockchainHeight(), 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-07-30 15:22:07 +00:00
" blockchain height: " << getCurrentBlockchainHeight();
2014-05-15 16:40:40 +00:00
bvc.m_verifivation_failed = true;
return false;
}
2014-08-13 10:51:37 +00:00
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
2015-07-30 15:22:07 +00:00
uint32_t mainPrevHeight = 0;
const bool mainPrev = m_blockIndex.getBlockHeight(b.previousBlockHash, mainPrevHeight);
const auto it_prev = m_alternative_chains.find(b.previousBlockHash);
2014-08-13 10:51:37 +00:00
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);
2015-07-30 15:22:07 +00:00
alt_it = m_alternative_chains.find(alt_it->second.bl.previousBlockHash);
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; }
2015-07-30 15:22:07 +00:00
Crypto::Hash h = NULL_HASH;
2014-03-03 22:07:58 +00:00
get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h);
2015-07-30 15:22:07 +00:00
if (!(h == alt_chain.front()->second.bl.previousBlockHash)) { 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; }
2015-07-30 15:22:07 +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; }
2015-07-30 15:22:07 +00:00
m_orthanBlocksIndex.add(bei.bl);
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;
2015-07-30 15:22:07 +00:00
if (sendNewAlternativeBlockMessage) {
sendMessage(BlockchainMessage(NewAlternativeBlockMessage(id)));
2015-07-30 15:22:07 +00:00
}
2014-05-15 16:40:40 +00:00
return true;
2014-03-03 22:07:58 +00:00
}
2015-07-30 15:22:07 +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-07-30 15:22:07 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain::getBlocks(uint32_t start_offset, uint32_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);
2015-07-30 15:22:07 +00:00
std::list<Crypto::Hash> missed_ids;
getTransactions(m_blocks[i].bl.transactionHashes, 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
2015-07-30 15:22:07 +00:00
bool Blockchain::getBlocks(uint32_t start_offset, uint32_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
2015-07-30 15:22:07 +00:00
for (uint32_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
2015-07-30 15:22:07 +00:00
bool Blockchain::handleGetObjects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { //Deprecated. Should be removed with CryptoNoteProtocolHandler.
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2015-07-30 15:22:07 +00:00
rsp.current_blockchain_height = getCurrentBlockchainHeight();
2014-08-13 10:51:37 +00:00
std::list<Block> blocks;
2015-07-30 15:22:07 +00:00
getBlocks(arg.blocks, blocks, rsp.missed_ids);
2014-03-03 22:07:58 +00:00
2014-06-20 15:56:33 +00:00
for (const auto& bl : blocks) {
2015-07-30 15:22:07 +00:00
std::list<Crypto::Hash> missed_tx_id;
2014-08-13 10:51:37 +00:00
std::list<Transaction> txs;
2015-07-30 15:22:07 +00:00
getTransactions(bl.transactionHashes, txs, rsp.missed_ids);
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; } //WTF???
2014-06-20 15:56:33 +00:00
rsp.blocks.push_back(block_complete_entry());
block_complete_entry& e = rsp.blocks.back();
//pack block
2015-07-30 15:22:07 +00:00
e.block = asString(toBinaryArray(bl));
2014-06-20 15:56:33 +00:00
//pack transactions
2014-08-13 10:51:37 +00:00
for (Transaction& tx : txs) {
2015-07-30 15:22:07 +00:00
e.txs.push_back(asString(toBinaryArray(tx)));
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
//get another transactions, if need
2014-08-13 10:51:37 +00:00
std::list<Transaction> txs;
2015-07-30 15:22:07 +00:00
getTransactions(arg.txs, txs, rsp.missed_ids);
2014-03-03 22:07:58 +00:00
//pack aside transactions
2014-06-20 15:56:33 +00:00
for (const auto& tx : txs) {
2015-07-30 15:22:07 +00:00
rsp.txs.push_back(asString(toBinaryArray(tx)));
2014-06-20 15:56:33 +00:00
}
2014-03-03 22:07:58 +00:00
return true;
}
2015-07-30 15:22:07 +00:00
bool Blockchain::getAlternativeBlocks(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
2015-07-30 15:22:07 +00:00
uint32_t Blockchain::getAlternativeBlocksCount() {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2015-07-30 15:22:07 +00:00
return static_cast<uint32_t>(m_alternative_chains.size());
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain::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-07-30 15:22:07 +00:00
if (!(tx.outputs.size() > amount_outs[i].second)) {
2015-05-27 12:08:46 +00:00
logger(ERROR, BRIGHT_RED) << "internal error: in global outs index, transaction out index="
2015-07-30 15:22:07 +00:00
<< amount_outs[i].second << " more than transaction outputs = " << tx.outputs.size() << ", for tx id = " << getObjectHash(tx); return false;
2015-05-27 12:08:46 +00:00
}
2015-07-30 15:22:07 +00:00
if (!(tx.outputs[amount_outs[i].second].target.type() == typeid(KeyOutput))) { 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());
2015-07-30 15:22:07 +00:00
oen.global_amount_index = static_cast<uint32_t>(i);
oen.out_key = boost::get<KeyOutput>(tx.outputs[amount_outs[i].second].target).key;
2014-03-03 22:07:58 +00:00
return true;
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
size_t Blockchain::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;
2015-07-30 15:22:07 +00:00
if (amount_outs[i].first.block + m_currency.minedMoneyUnlockWindow() <= getCurrentBlockchainHeight()) {
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
2015-07-30 15:22:07 +00:00
bool Blockchain::getRandomOutsByAmount(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) {
2015-07-30 15:22:07 +00:00
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-07-30 15:22:07 +00:00
uint32_t Blockchain::findBlockchainSupplement(const std::vector<Crypto::Hash>& qblock_ids) {
assert(!qblock_ids.empty());
assert(qblock_ids.back() == m_blockIndex.getBlockId(0));
2014-03-03 22:07:58 +00:00
2015-07-30 15:22:07 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
uint32_t blockIndex;
// assert above guarantees that method returns true
m_blockIndex.findSupplement(qblock_ids, blockIndex);
return blockIndex;
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
uint64_t Blockchain::blockDifficulty(size_t i) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2015-07-30 15:22:07 +00:00
if (!(i < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "wrong block index i = " << i << " at Blockchain::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-07-30 15:22:07 +00:00
void Blockchain::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)
2015-07-30 15:22:07 +00:00
<< "\ndifficulty\t\t" << blockDifficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.transactionHashes.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
2015-07-30 15:22:07 +00:00
void Blockchain::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
2015-07-30 15:22:07 +00:00
std::vector<Crypto::Hash> blockIds = m_blockIndex.getBlockIds(0, std::numeric_limits<uint32_t>::max());
logger(INFO, BRIGHT_WHITE) << "Current blockchain index:";
2014-08-13 10:51:37 +00:00
size_t height = 0;
for (auto i = blockIds.begin(); i != blockIds.end(); ++i, ++height) {
2015-07-30 15:22:07 +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
2015-07-30 15:22:07 +00:00
void Blockchain::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++) {
2015-07-30 15:22:07 +00:00
ss << "\t" << getObjectHash(transactionByIndex(vals[i].first).tx) << ": " << vals[i].second << ENDL;
2014-06-20 15:56:33 +00:00
}
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
2015-07-30 15:22:07 +00:00
std::vector<Crypto::Hash> Blockchain::findBlockchainSupplement(const std::vector<Crypto::Hash>& remoteBlockIds, size_t maxCount,
uint32_t& totalBlockCount, uint32_t& startBlockIndex) {
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
assert(!remoteBlockIds.empty());
assert(remoteBlockIds.back() == m_blockIndex.getBlockId(0));
2014-06-20 15:56:33 +00:00
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2015-07-30 15:22:07 +00:00
totalBlockCount = getCurrentBlockchainHeight();
startBlockIndex = findBlockchainSupplement(remoteBlockIds);
2014-03-03 22:07:58 +00:00
2015-07-30 15:22:07 +00:00
return m_blockIndex.getBlockIds(startBlockIndex, static_cast<uint32_t>(maxCount));
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain::haveBlock(const Crypto::Hash& 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
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;
}
2015-07-30 15:22:07 +00:00
size_t Blockchain::getTotalTransactions() {
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
}
2015-07-30 15:22:07 +00:00
bool Blockchain::getTransactionOutputGlobalIndexes(const Crypto::Hash& tx_id, std::vector<uint32_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
2015-07-30 15:22:07 +00:00
bool Blockchain::get_out_by_msig_gindex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
auto it = m_multisignatureOutputs.find(amount);
if (it == m_multisignatureOutputs.end()) {
return false;
}
if (it->second.size() <= gindex) {
return false;
}
auto msigUsage = it->second[gindex];
auto& targetOut = transactionByIndex(msigUsage.transactionIndex).tx.outputs[msigUsage.outputIndex].target;
if (targetOut.type() != typeid(MultisignatureOutput)) {
return false;
}
out = boost::get<MultisignatureOutput>(targetOut);
return true;
}
bool Blockchain::checkTransactionInputs(const Transaction& tx, uint32_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)
2015-07-30 15:22:07 +00:00
tail->id = getTailId(tail->height);
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
bool res = checkTransactionInputs(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
2015-07-30 15:22:07 +00:00
bool Blockchain::haveTransactionKeyImagesAsSpent(const Transaction &tx) {
for (const auto& in : tx.inputs) {
if (in.type() == typeid(KeyInput)) {
if (have_tx_keyimg_as_spent(boost::get<KeyInput>(in).keyImage)) {
2014-08-13 10:51:37 +00:00
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
2015-07-30 15:22:07 +00:00
bool Blockchain::checkTransactionInputs(const Transaction& tx, uint32_t* pmax_used_block_height) {
Crypto::Hash tx_prefix_hash = getObjectHash(*static_cast<const TransactionPrefix*>(&tx));
return checkTransactionInputs(tx, tx_prefix_hash, pmax_used_block_height);
2014-03-03 22:07:58 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain::checkTransactionInputs(const Transaction& tx, const Crypto::Hash& tx_prefix_hash, uint32_t* pmax_used_block_height) {
2014-08-13 10:51:37 +00:00
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
2015-07-30 15:22:07 +00:00
Crypto::Hash transactionHash = getObjectHash(tx);
for (const auto& txin : tx.inputs) {
2014-08-13 10:51:37 +00:00
assert(inputIndex < tx.signatures.size());
2015-07-30 15:22:07 +00:00
if (txin.type() == typeid(KeyInput)) {
const KeyInput& in_to_key = boost::get<KeyInput>(txin);
if (!(!in_to_key.outputIndexes.empty())) { logger(ERROR, BRIGHT_RED) << "empty in_to_key.outputIndexes in transaction with id " << getObjectHash(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;
2015-07-30 15:22:07 +00:00
} else if (txin.type() == typeid(MultisignatureInput)) {
if (!validateInput(::boost::get<MultisignatureInput>(txin), transactionHash, tx_prefix_hash, tx.signatures[inputIndex])) {
2014-08-13 10:51:37 +00:00
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
2015-07-30 15:22:07 +00:00
bool Blockchain::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
2015-07-30 15:22:07 +00:00
if (getCurrentBlockchainHeight() - 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
2015-07-30 15:22:07 +00:00
bool Blockchain::check_tx_input(const KeyInput& txin, const Crypto::Hash& tx_prefix_hash, const std::vector<Crypto::Signature>& sig, uint32_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 {
2015-07-30 15:22:07 +00:00
std::vector<const Crypto::PublicKey *>& m_results_collector;
Blockchain& m_bch;
2015-05-27 12:08:46 +00:00
LoggerRef logger;
2015-07-30 15:22:07 +00:00
outputs_visitor(std::vector<const Crypto::PublicKey *>& results_collector, Blockchain& bch, ILogger& logger) :m_results_collector(results_collector), m_bch(bch), logger(logger, "outputs_visitor") {
2015-05-27 12:08:46 +00:00
}
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-07-30 15:22:07 +00:00
if (out.target.type() != typeid(KeyOutput)) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
"Output have wrong type id, which=" << out.target.which();
2014-03-03 22:07:58 +00:00
return false;
}
2015-07-30 15:22:07 +00:00
m_results_collector.push_back(&boost::get<KeyOutput>(out.target).key);
2014-03-03 22:07:58 +00:00
return true;
}
};
//check ring signature
2015-07-30 15:22:07 +00:00
std::vector<const Crypto::PublicKey *> output_keys;
2015-05-27 12:08:46 +00:00
outputs_visitor vi(output_keys, *this, logger.getLogger());
2015-07-30 15:22:07 +00:00
if (!scanOutputKeysForIndexes(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) <<
2015-07-30 15:22:07 +00:00
" and count indexes " << txin.outputIndexes.size();
2014-03-03 22:07:58 +00:00
return false;
}
2015-07-30 15:22:07 +00:00
if (txin.outputIndexes.size() != output_keys.size()) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
2015-07-30 15:22:07 +00:00
"Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.outputIndexes.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
}
2015-07-30 15:22:07 +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
2015-07-30 15:22:07 +00:00
uint64_t Blockchain::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
2015-07-30 15:22:07 +00:00
bool Blockchain::check_block_timestamp_main(const Block& b) {
2014-08-13 10:51:37 +00:00
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
2015-07-30 15:22:07 +00:00
bool Blockchain::check_block_timestamp(std::vector<uint64_t> timestamps, const Block& b) {
2014-08-13 10:51:37 +00:00
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;
}
2015-07-30 15:22:07 +00:00
bool Blockchain::checkCumulativeBlockSize(const Crypto::Hash& blockId, size_t cumulativeBlockSize, uint64_t height) {
2014-08-13 10:51:37 +00:00
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.
2015-07-30 15:22:07 +00:00
bool Blockchain::getBlockCumulativeSize(const Block& block, size_t& cumulativeSize) {
2014-08-13 10:51:37 +00:00
std::vector<Transaction> blockTxs;
2015-07-30 15:22:07 +00:00
std::vector<Crypto::Hash> missedTxs;
getTransactions(block.transactionHashes, blockTxs, missedTxs, true);
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
cumulativeSize = getObjectBinarySize(block.baseTransaction);
2014-08-13 10:51:37 +00:00
for (const Transaction& tx : blockTxs) {
2015-07-30 15:22:07 +00:00
cumulativeSize += getObjectBinarySize(tx);
2014-08-13 10:51:37 +00:00
}
return missedTxs.empty();
}
2014-08-14 15:41:44 +00:00
// Precondition: m_blockchain_lock is locked.
2015-07-30 15:22:07 +00:00
bool Blockchain::update_next_comulative_size_limit() {
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);
if (median <= m_currency.blockGrantedFullRewardZone()) {
median = m_currency.blockGrantedFullRewardZone();
2014-08-14 15:41:44 +00:00
}
2014-06-20 15:56:33 +00:00
m_current_block_cumul_sz_limit = median * 2;
return true;
}
2015-07-30 15:22:07 +00:00
bool Blockchain::addNewBlock(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_;
2015-07-30 15:22:07 +00:00
Crypto::Hash id;
2014-08-13 10:51:37 +00:00
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);
2015-07-30 15:22:07 +00:00
if (haveBlock(id)) {
2015-05-27 12:08:46 +00:00
logger(TRACE) << "block with id = " << id << " already exists";
bvc.m_already_exists = true;
return false;
}
//check that block refers to chain tail
2015-07-30 15:22:07 +00:00
if (!(bl.previousBlockHash == getTailId())) {
2015-05-27 12:08:46 +00:00
//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);
2015-07-30 15:22:07 +00:00
if (add_result) {
sendMessage(BlockchainMessage(NewBlockMessage(id)));
2015-07-30 15:22:07 +00:00
}
2015-05-27 12:08:46 +00:00
}
}
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
}
2015-07-30 15:22:07 +00:00
const Blockchain::TransactionEntry& Blockchain::transactionByIndex(TransactionIndex index) {
2014-06-20 15:56:33 +00:00
return m_blocks[index.block].transactions[index.transaction];
}
2015-07-30 15:22:07 +00:00
bool Blockchain::pushBlock(const Block& blockData, block_verification_context& bvc) {
std::vector<Transaction> transactions;
if (!loadTransactions(blockData, transactions)) {
bvc.m_verifivation_failed = true;
return false;
}
if (!pushBlock(blockData, transactions, bvc)) {
saveTransactions(transactions);
return false;
}
return true;
}
bool Blockchain::pushBlock(const Block& blockData, const std::vector<Transaction>& transactions, 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
2015-07-30 15:22:07 +00:00
Crypto::Hash blockHash = get_block_hash(blockData);
2014-06-20 15:56:33 +00:00
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;
}
2015-07-30 15:22:07 +00:00
if (blockData.previousBlockHash != getTailId()) {
2015-05-27 12:08:46 +00:00
logger(INFO, BRIGHT_WHITE) <<
2015-07-30 15:22:07 +00:00
"Block " << blockHash << " has wrong previousBlockHash: " << blockData.previousBlockHash << ", expected: " << getTailId();
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();
2015-07-30 15:22:07 +00:00
difficulty_type currentDifficulty = getDifficultyForNextBlock();
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();
2015-07-30 15:22:07 +00:00
if (!(currentDifficulty)) {
logger(ERROR, BRIGHT_RED) << "!!!!!!!!! difficulty overhead !!!!!!!!!";
return false;
2015-05-27 12:08:46 +00:00
}
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
2015-05-27 12:08:46 +00:00
auto longhashTimeStart = std::chrono::steady_clock::now();
2015-07-30 15:22:07 +00:00
Crypto::Hash proof_of_work = NULL_HASH;
if (m_checkpoints.is_in_checkpoint_zone(getCurrentBlockchainHeight())) {
if (!m_checkpoints.check_block(getCurrentBlockchainHeight(), 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
2015-07-30 15:22:07 +00:00
if (!prevalidate_miner_transaction(blockData, static_cast<uint32_t>(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
2015-07-30 15:22:07 +00:00
Crypto::Hash minerTransactionHash = getObjectHash(blockData.baseTransaction);
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-07-30 15:22:07 +00:00
block.transactions[0].tx = blockData.baseTransaction;
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);
2015-07-30 15:22:07 +00:00
size_t coinbase_blob_size = getObjectBinarySize(blockData.baseTransaction);
2014-03-03 22:07:58 +00:00
size_t cumulative_block_size = coinbase_blob_size;
uint64_t fee_summary = 0;
2015-07-30 15:22:07 +00:00
for (size_t i = 0; i < transactions.size(); ++i) {
const Crypto::Hash& tx_id = blockData.transactionHashes[i];
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;
2015-07-30 15:22:07 +00:00
block.transactions.back().tx = transactions[i];
2014-06-20 15:56:33 +00:00
2015-07-30 15:22:07 +00:00
blob_size = toBinaryArray(block.transactions.back().tx).size();
fee = getInputAmount(block.transactions.back().tx) - getOutputAmount(block.transactions.back().tx);
if (!checkTransactionInputs(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;
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;
2015-07-30 15:22:07 +00:00
if (!validate_miner_transaction(blockData, static_cast<uint32_t>(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
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;
}
2015-07-30 15:22:07 +00:00
bool Blockchain::pushBlock(BlockEntry& block) {
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);
2015-07-30 15:22:07 +00:00
m_timestampIndex.add(block.bl.timestamp, blockHash);
m_generatedTransactionsIndex.add(block.bl);
2014-08-13 10:51:37 +00:00
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
2015-07-30 15:22:07 +00:00
void Blockchain::popBlock(const Crypto::Hash& blockHash) {
2014-06-20 15:56:33 +00:00
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;
}
2015-07-30 15:22:07 +00:00
std::vector<Transaction> transactions(m_blocks.back().transactions.size() - 1);
for (size_t i = 0; i < m_blocks.back().transactions.size() - 1; ++i) {
transactions[i] = m_blocks.back().transactions[1 + i].tx;
}
saveTransactions(transactions);
popTransactions(m_blocks.back(), getObjectHash(m_blocks.back().bl.baseTransaction));
m_timestampIndex.remove(m_blocks.back().bl.timestamp, blockHash);
m_generatedTransactionsIndex.remove(m_blocks.back().bl);
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());
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
bool Blockchain::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;
}
2015-07-30 15:22:07 +00:00
for (size_t i = 0; i < transaction.tx.inputs.size(); ++i) {
if (transaction.tx.inputs[i].type() == typeid(KeyInput)) {
auto result = m_spent_keys.insert(::boost::get<KeyInput>(transaction.tx.inputs[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) {
2015-07-30 15:22:07 +00:00
m_spent_keys.erase(::boost::get<KeyInput>(transaction.tx.inputs[i - 1 - j]).keyImage);
2014-06-20 15:56:33 +00:00
}
m_transactionMap.erase(transactionHash);
return false;
}
}
}
2015-07-30 15:22:07 +00:00
for (const auto& inv : transaction.tx.inputs) {
if (inv.type() == typeid(MultisignatureInput)) {
const MultisignatureInput& in = ::boost::get<MultisignatureInput>(inv);
2014-08-13 10:51:37 +00:00
auto& amountOutputs = m_multisignatureOutputs[in.amount];
amountOutputs[in.outputIndex].isUsed = true;
}
}
2015-07-30 15:22:07 +00:00
transaction.m_global_output_indexes.resize(transaction.tx.outputs.size());
for (uint16_t output = 0; output < transaction.tx.outputs.size(); ++output) {
if (transaction.tx.outputs[output].target.type() == typeid(KeyOutput)) {
auto& amountOutputs = m_outputs[transaction.tx.outputs[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));
2015-07-30 15:22:07 +00:00
} else if (transaction.tx.outputs[output].target.type() == typeid(MultisignatureOutput)) {
auto& amountOutputs = m_multisignatureOutputs[transaction.tx.outputs[output].amount];
2015-05-27 12:08:46 +00:00
transaction.m_global_output_indexes[output] = static_cast<uint32_t>(amountOutputs.size());
2015-07-30 15:22:07 +00:00
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
}
2015-07-30 15:22:07 +00:00
m_paymentIdIndex.add(transaction.tx);
2014-06-20 15:56:33 +00:00
return true;
}
2015-07-30 15:22:07 +00:00
void Blockchain::popTransaction(const Transaction& transaction, const Crypto::Hash& transactionHash) {
2014-06-20 15:56:33 +00:00
TransactionIndex transactionIndex = m_transactionMap.at(transactionHash);
2015-07-30 15:22:07 +00:00
for (size_t outputIndex = 0; outputIndex < transaction.outputs.size(); ++outputIndex) {
const TransactionOutput& output = transaction.outputs[transaction.outputs.size() - 1 - outputIndex];
if (output.target.type() == typeid(KeyOutput)) {
2014-08-13 10:51:37 +00:00
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
2015-07-30 15:22:07 +00:00
if (amountOutputs->second.back().second != transaction.outputs.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);
}
2015-07-30 15:22:07 +00:00
} else if (output.target.type() == typeid(MultisignatureOutput)) {
2014-08-13 10:51:37 +00:00
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;
}
2015-07-30 15:22:07 +00:00
if (amountOutputs->second.back().outputIndex != transaction.outputs.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
}
}
2015-07-30 15:22:07 +00:00
for (auto& input : transaction.inputs) {
if (input.type() == typeid(KeyInput)) {
size_t count = m_spent_keys.erase(::boost::get<KeyInput>(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
}
2015-07-30 15:22:07 +00:00
} else if (input.type() == typeid(MultisignatureInput)) {
const MultisignatureInput& in = ::boost::get<MultisignatureInput>(input);
2014-08-13 10:51:37 +00:00
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
}
2015-07-30 15:22:07 +00:00
m_paymentIdIndex.remove(transaction);
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
}
}
2015-07-30 15:22:07 +00:00
void Blockchain::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) {
2015-07-30 15:22:07 +00:00
popTransaction(block.transactions[block.transactions.size() - 1 - i].tx, block.bl.transactionHashes[block.transactions.size() - 2 - i]);
2014-06-20 15:56:33 +00:00
}
2015-07-30 15:22:07 +00:00
popTransaction(block.bl.baseTransaction, minerTransactionHash);
2014-08-13 10:51:37 +00:00
}
2015-07-30 15:22:07 +00:00
bool Blockchain::validateInput(const MultisignatureInput& input, const Crypto::Hash& transactionHash, const Crypto::Hash& transactionPrefixHash, const std::vector<Crypto::Signature>& transactionSignatures) {
assert(input.signatureCount == transactionSignatures.size());
2014-08-13 10:51:37 +00:00
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;
}
2015-07-30 15:22:07 +00:00
assert(outputTransaction.outputs[outputIndex.outputIndex].amount == input.amount);
assert(outputTransaction.outputs[outputIndex.outputIndex].target.type() == typeid(MultisignatureOutput));
const MultisignatureOutput& output = ::boost::get<MultisignatureOutput>(outputTransaction.outputs[outputIndex.outputIndex].target);
if (input.signatureCount != output.requiredSignatureCount) {
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;
}
2015-07-30 15:22:07 +00:00
size_t inputSignatureIndex = 0;
size_t outputKeyIndex = 0;
while (inputSignatureIndex < input.signatureCount) {
2014-08-13 10:51:37 +00:00
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;
}
2015-07-30 15:22:07 +00:00
if (Crypto::check_signature(transactionPrefixHash, output.keys[outputKeyIndex], transactionSignatures[inputSignatureIndex])) {
2014-08-13 10:51:37 +00:00
++inputSignatureIndex;
}
++outputKeyIndex;
}
return true;
}
2015-07-30 15:22:07 +00:00
bool Blockchain::getLowerBound(uint64_t timestamp, uint64_t startOffset, uint32_t& height) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2015-07-30 15:22:07 +00:00
assert(startOffset < m_blocks.size());
2014-08-13 10:51:37 +00:00
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;
}
2015-07-30 15:22:07 +00:00
height = static_cast<uint32_t>(std::distance(m_blocks.begin(), bound));
2014-08-13 10:51:37 +00:00
return true;
}
2015-07-30 15:22:07 +00:00
std::vector<Crypto::Hash> Blockchain::getBlockIds(uint32_t startHeight, uint32_t maxCount) {
2015-05-27 12:08:46 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
2015-07-30 15:22:07 +00:00
return m_blockIndex.getBlockIds(startHeight, maxCount);
2014-06-20 15:56:33 +00:00
}
2015-05-27 12:08:46 +00:00
2015-07-30 15:22:07 +00:00
bool Blockchain::getBlockContainingTransaction(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight) {
2015-07-15 12:23:00 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
auto it = m_transactionMap.find(txId);
2015-07-30 15:22:07 +00:00
if (it == m_transactionMap.end()) {
2015-07-15 12:23:00 +00:00
return false;
} else {
blockHeight = m_blocks[it->second.block].height;
2015-07-30 15:22:07 +00:00
blockId = getBlockIdByHeight(blockHeight);
2015-07-15 12:23:00 +00:00
return true;
}
}
2015-07-30 15:22:07 +00:00
bool Blockchain::getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins) {
2015-07-15 12:23:00 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
// try to find block in main chain
2015-07-30 15:22:07 +00:00
uint32_t height = 0;
2015-07-15 12:23:00 +00:00
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;
}
2015-07-30 15:22:07 +00:00
bool Blockchain::getBlockSize(const Crypto::Hash& hash, size_t& size) {
2015-07-15 12:23:00 +00:00
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
// try to find block in main chain
2015-07-30 15:22:07 +00:00
uint32_t height = 0;
2015-07-15 12:23:00 +00:00
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;
}
2015-07-30 15:22:07 +00:00
bool Blockchain::getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair<Crypto::Hash, size_t>& outputReference) {
2015-07-15 12:23:00 +00:00
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;
2015-07-30 15:22:07 +00:00
outputReference.first = getObjectHash(outputTransaction);
2015-07-15 12:23:00 +00:00
outputReference.second = outputIndex.outputIndex;
return true;
}
2015-07-30 15:22:07 +00:00
bool Blockchain::storeBlockchainIndices() {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
logger(INFO, BRIGHT_WHITE) << "Saving blockchain indices...";
BlockchainIndicesSerializer ser(*this, getTailId(), logger.getLogger());
if (!storeToBinaryFile(ser, appendPath(m_config_folder, m_currency.blockchinIndicesFileName()))) {
logger(ERROR, BRIGHT_RED) << "Failed to save blockchain indices";
return false;
}
return true;
}
bool Blockchain::loadBlockchainIndices() {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
logger(INFO, BRIGHT_WHITE) << "Loading blockchain indices for BlockchainExplorer...";
BlockchainIndicesSerializer loader(*this, get_block_hash(m_blocks.back().bl), logger.getLogger());
loadFromBinaryFile(loader, appendPath(m_config_folder, m_currency.blockchinIndicesFileName()));
if (!loader.loaded()) {
logger(WARNING, BRIGHT_YELLOW) << "No actual blockchain indices for BlockchainExplorer found, rebuilding...";
std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now();
m_paymentIdIndex.clear();
m_timestampIndex.clear();
m_generatedTransactionsIndex.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];
m_timestampIndex.add(block.bl.timestamp, get_block_hash(block.bl));
m_generatedTransactionsIndex.add(block.bl);
for (uint16_t t = 0; t < block.transactions.size(); ++t) {
const TransactionEntry& transaction = block.transactions[t];
m_paymentIdIndex.add(transaction.tx);
}
}
std::chrono::duration<double> duration = std::chrono::steady_clock::now() - timePoint;
logger(INFO, BRIGHT_WHITE) << "Rebuilding blockchain indices took: " << duration.count();
}
return true;
}
bool Blockchain::getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
return m_generatedTransactionsIndex.find(height, generatedTransactions);
}
bool Blockchain::getOrphanBlockIdsByHeight(uint32_t height, std::vector<Crypto::Hash>& blockHashes) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
return m_orthanBlocksIndex.find(height, blockHashes);
}
bool Blockchain::getBlockIdsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector<Crypto::Hash>& hashes, uint32_t& blocksNumberWithinTimestamps) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
return m_timestampIndex.find(timestampBegin, timestampEnd, blocksNumberLimit, hashes, blocksNumberWithinTimestamps);
}
bool Blockchain::getTransactionIdsByPaymentId(const Crypto::Hash& paymentId, std::vector<Crypto::Hash>& transactionHashes) {
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
return m_paymentIdIndex.find(paymentId, transactionHashes);
}
bool Blockchain::loadTransactions(const Block& block, std::vector<Transaction>& transactions) {
transactions.resize(block.transactionHashes.size());
size_t transactionSize;
uint64_t fee;
for (size_t i = 0; i < block.transactionHashes.size(); ++i) {
if (!m_tx_pool.take_tx(block.transactionHashes[i], transactions[i], transactionSize, fee)) {
tx_verification_context context;
for (size_t j = 0; j < i; ++j) {
if (!m_tx_pool.add_tx(transactions[i - 1 - j], context, true)) {
throw std::runtime_error("Blockchain::loadTransactions, failed to add transaction to pool");
}
}
return false;
}
}
return true;
}
void Blockchain::saveTransactions(const std::vector<Transaction>& transactions) {
tx_verification_context context;
for (size_t i = 0; i < transactions.size(); ++i) {
if (!m_tx_pool.add_tx(transactions[transactions.size() - 1 - i], context, true)) {
throw std::runtime_error("Blockchain::saveTransactions, failed to add transaction to pool");
}
}
}
bool Blockchain::addMessageQueue(MessageQueue<BlockchainMessage>& messageQueue) {
return m_messageQueueList.insert(messageQueue);
}
bool Blockchain::removeMessageQueue(MessageQueue<BlockchainMessage>& messageQueue) {
return m_messageQueueList.remove(messageQueue);
}
void Blockchain::sendMessage(const BlockchainMessage& message) {
for (IntrusiveLinkedList<MessageQueue<BlockchainMessage>>::iterator iter = m_messageQueueList.begin(); iter != m_messageQueueList.end(); ++iter) {
iter->push(message);
}
}
bool Blockchain::isBlockInMainChain(const Crypto::Hash& blockId) {
return m_blockIndex.hasBlock(blockId);
}
2015-05-27 12:08:46 +00:00
}