From 198323aaf291965172ea5e4c8e76d56d1d716f3f Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Fri, 20 Jun 2014 16:56:33 +0100 Subject: [PATCH] Optimized blockchain storage --- ReleaseNotes.txt | 5 + .../epee/include/net/abstract_tcp_server2.inl | 2 +- .../net/levin_protocol_handler_async.h | 4 +- src/crypto/slow-hash.c | 5 +- src/cryptonote_config.h | 4 +- src/cryptonote_core/SwappedMap.cpp | 1 + src/cryptonote_core/SwappedMap.h | 350 ++++ src/cryptonote_core/SwappedVector.cpp | 1 + src/cryptonote_core/SwappedVector.h | 284 +++ src/cryptonote_core/blockchain_storage.cpp | 1794 +++++++++-------- src/cryptonote_core/blockchain_storage.h | 305 +-- .../blockchain_storage_boost_serialization.h | 3 +- src/cryptonote_core/checkpoints_create.h | 1 + src/cryptonote_core/cryptonote_basic_impl.cpp | 29 +- src/cryptonote_core/cryptonote_basic_impl.h | 1 + src/cryptonote_core/cryptonote_core.cpp | 55 +- src/cryptonote_core/cryptonote_core.h | 7 +- .../cryptonote_format_utils.cpp | 22 +- src/cryptonote_core/cryptonote_format_utils.h | 3 +- src/cryptonote_core/miner.cpp | 5 +- src/p2p/net_node.inl | 2 +- src/rpc/core_rpc_server.cpp | 38 +- src/serialization/binary_utils.h | 2 + src/serialization/crypto.h | 2 + src/serialization/json_utils.h | 2 + src/serialization/vector.h | 7 + src/version.h.in | 2 +- src/wallet/wallet_errors.h | 39 +- 28 files changed, 1817 insertions(+), 1158 deletions(-) create mode 100755 src/cryptonote_core/SwappedMap.cpp create mode 100755 src/cryptonote_core/SwappedMap.h create mode 100755 src/cryptonote_core/SwappedVector.cpp create mode 100755 src/cryptonote_core/SwappedVector.h diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 959851e5..90c74c3b 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,8 @@ +Release notes 0.8.10 + +- Optimized blockchain storage memory usage +- Various code improvements + Release notes 0.8.9 - JSON RPC v2.0 compatibility diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 403f5a3b..74f1e5c4 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -304,7 +304,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) if(m_send_que.size() > ABSTRACT_SERVER_SEND_QUE_MAX_COUNT) { send_guard.unlock(); - LOG_ERROR("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection"); +// LOG_ERROR("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection"); close(); return false; } diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index e7fb32fe..a286c472 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -512,7 +512,7 @@ public: CRITICAL_REGION_LOCAL1(m_invoke_response_handlers_lock); if(!m_pservice_endpoint->do_send(&head, sizeof(head))) { - LOG_ERROR_CC(m_connection_context, "Failed to do_send"); +// LOG_ERROR_CC(m_connection_context, "Failed to do_send"); err_code = LEVIN_ERROR_CONNECTION; break; } @@ -635,7 +635,7 @@ public: CRITICAL_REGION_BEGIN(m_send_lock); if(!m_pservice_endpoint->do_send(&head, sizeof(head))) { - LOG_ERROR_CC(m_connection_context, "Failed to do_send()"); +// LOG_ERROR_CC(m_connection_context, "Failed to do_send()"); return -1; } diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 351314bf..468ba644 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -150,7 +150,6 @@ void cn_slow_hash(const void *data, size_t length, char *hash) uint8_t a[AES_BLOCK_SIZE]; uint8_t b[AES_BLOCK_SIZE]; uint8_t d[AES_BLOCK_SIZE]; - uint8_t aes_key[AES_KEY_SIZE]; RDATA_ALIGN16 uint8_t expandedKey[256]; union cn_slow_hash_state state; @@ -201,8 +200,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash) for(i = 0; i < ITER / 2; i++) { - #define TOTALBLOCKS (MEMORY / AES_BLOCK_SIZE) - #define state_index(x) (((*((uint64_t *)x) >> 4) & (TOTALBLOCKS - 1)) << 4) + #define TOTALBLOCKS (MEMORY / AES_BLOCK_SIZE) + #define state_index(x) (((*((uint64_t *)x) >> 4) & (TOTALBLOCKS - 1)) << 4) // Iteration 1 p = &long_state[state_index(a)]; diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 6b3db89a..958639a0 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -19,6 +19,7 @@ // MONEY_SUPPLY - total number coins to be generated #define MONEY_SUPPLY ((uint64_t)(-1)) +#define EMISSION_SPEED_FACTOR (18) #define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 10000 //size of block (bytes) after which reward for block calculated using block size @@ -29,9 +30,6 @@ #define DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6) -#define ORPHANED_BLOCKS_MAX_COUNT 100 - - #define DIFFICULTY_TARGET 120 // seconds #define DIFFICULTY_WINDOW 720 // blocks #define DIFFICULTY_LAG 15 // !!! diff --git a/src/cryptonote_core/SwappedMap.cpp b/src/cryptonote_core/SwappedMap.cpp new file mode 100755 index 00000000..44ed6ef2 --- /dev/null +++ b/src/cryptonote_core/SwappedMap.cpp @@ -0,0 +1 @@ +#include "SwappedMap.h" diff --git a/src/cryptonote_core/SwappedMap.h b/src/cryptonote_core/SwappedMap.h new file mode 100755 index 00000000..f03bf5c9 --- /dev/null +++ b/src/cryptonote_core/SwappedMap.h @@ -0,0 +1,350 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template class SwappedMap { +private: + struct Descriptor { + uint64_t offset; + uint64_t index; + }; + +public: + typedef typename std::pair value_type; + + class const_iterator { + public: + //typedef ptrdiff_t difference_type; + //typedef std::bidirectional_iterator_tag iterator_category; + //typedef std::pair* pointer; + //typedef std::pair& reference; + //typedef std::pair value_type; + + const_iterator(SwappedMap* swappedMap, typename std::unordered_map::const_iterator descriptorsIterator) : m_swappedMap(swappedMap), m_descriptorsIterator(descriptorsIterator) { + } + + const_iterator& operator++() { + ++m_descriptorsIterator; + return *this; + } + + bool operator !=(const_iterator other) const { + return m_descriptorsIterator != other.m_descriptorsIterator; + } + + bool operator ==(const_iterator other) const { + return m_descriptorsIterator == other.m_descriptorsIterator; + } + + const std::pair& operator*() const { + return *m_swappedMap->load(m_descriptorsIterator->first, m_descriptorsIterator->second.offset); + } + + const std::pair* operator->() const { + return m_swappedMap->load(m_descriptorsIterator->first, m_descriptorsIterator->second.offset); + } + + typename std::unordered_map::const_iterator innerIterator() const { + return m_descriptorsIterator; + } + + private: + SwappedMap* m_swappedMap; + typename std::unordered_map::const_iterator m_descriptorsIterator; + }; + + typedef const_iterator iterator; + + SwappedMap(); + //SwappedMap(const SwappedMap&) = delete; + ~SwappedMap(); + //SwappedMap& operator=(const SwappedMap&) = delete; + + bool open(const std::string& itemFileName, const std::string& indexFileName, size_t poolSize); + void close(); + + uint64_t size() const; + const_iterator begin(); + const_iterator end(); + size_t count(const Key& key) const; + const_iterator find(const Key& key); + + void clear(); + void erase(const_iterator iterator); + std::pair insert(const std::pair& value); + +private: + std::fstream m_itemsFile; + std::fstream m_indexesFile; + size_t m_poolSize; + std::unordered_map m_descriptors; + uint64_t m_itemsFileSize; + std::unordered_map m_items; + std::list m_cache; + std::unordered_map::iterator> m_cacheIterators; + uint64_t m_cacheHits; + uint64_t m_cacheMisses; + + std::pair* prepare(const Key& key); + const std::pair* load(const Key& key, uint64_t offset); +}; + +template SwappedMap::SwappedMap() { +} + +template SwappedMap::~SwappedMap() { + close(); +} + +template bool SwappedMap::open(const std::string& itemFileName, const std::string& indexFileName, size_t poolSize) { + if (poolSize == 0) { + return false; + } + + m_itemsFile.open(itemFileName, std::ios::in | std::ios::out | std::ios::binary); + m_indexesFile.open(indexFileName, std::ios::in | std::ios::out | std::ios::binary); + if (m_itemsFile && m_indexesFile) { + uint64_t count; + m_indexesFile.read(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + return false; + } + + std::unordered_map descriptors; + uint64_t itemsFileSize = 0; + for (uint64_t i = 0; i < count; ++i) { + bool valid; + m_indexesFile.read(reinterpret_cast(&valid), sizeof valid); + if (!m_indexesFile) { + return false; + } + + Key key; + m_indexesFile.read(reinterpret_cast(&key), sizeof key); + if (!m_indexesFile) { + return false; + } + + uint32_t itemSize; + m_indexesFile.read(reinterpret_cast(&itemSize), sizeof itemSize); + if (!m_indexesFile) { + return false; + } + + if (valid) { + Descriptor descriptor = { itemsFileSize, i }; + descriptors.insert(std::make_pair(key, descriptor)); + } + + itemsFileSize += itemSize; + } + + m_descriptors.swap(descriptors); + m_itemsFileSize = itemsFileSize; + } else { + m_itemsFile.open(itemFileName, std::ios::out | std::ios::binary); + m_itemsFile.close(); + m_itemsFile.open(itemFileName, std::ios::in | std::ios::out | std::ios::binary); + m_indexesFile.open(indexFileName, std::ios::out | std::ios::binary); + uint64_t count = 0; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + return false; + } + + m_indexesFile.close(); + m_indexesFile.open(indexFileName, std::ios::in | std::ios::out | std::ios::binary); + m_descriptors.clear(); + m_itemsFileSize = 0; + } + + m_poolSize = poolSize; + m_items.clear(); + m_cache.clear(); + m_cacheIterators.clear(); + m_cacheHits = 0; + m_cacheMisses = 0; + return true; +} + +template void SwappedMap::close() { + std::cout << "SwappedMap cache hits: " << m_cacheHits << ", misses: " << m_cacheMisses << " (" << std::fixed << std::setprecision(2) << static_cast(m_cacheMisses) / (m_cacheHits + m_cacheMisses) * 100 << "%)" << std::endl; +} + +template uint64_t SwappedMap::size() const { + return m_descriptors.size(); +} + +template typename SwappedMap::const_iterator SwappedMap::begin() { + return const_iterator(this, m_descriptors.cbegin()); +} + +template typename SwappedMap::const_iterator SwappedMap::end() { + return const_iterator(this, m_descriptors.cend()); +} + +template size_t SwappedMap::count(const Key& key) const { + return m_descriptors.count(key); +} + +template typename SwappedMap::const_iterator SwappedMap::find(const Key& key) { + return const_iterator(this, m_descriptors.find(key)); +} + +template void SwappedMap::clear() { + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::clear"); + } + + m_indexesFile.seekp(0); + uint64_t count = 0; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::clear"); + } + + m_descriptors.clear(); + m_itemsFileSize = 0; + m_items.clear(); + m_cache.clear(); + m_cacheIterators.clear(); +} + +template void SwappedMap::erase(const_iterator iterator) { + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::erase"); + } + + typename std::unordered_map::const_iterator descriptorsIterator = iterator.innerIterator(); + m_indexesFile.seekp(sizeof(uint64_t) + (sizeof(bool) + sizeof(Key) + sizeof(uint32_t)) * descriptorsIterator->second.index); + bool valid = false; + m_indexesFile.write(reinterpret_cast(&valid), sizeof valid); + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::erase"); + } + + m_descriptors.erase(descriptorsIterator); + auto cacheIteratorsIterator = m_cacheIterators.find(descriptorsIterator->first); + if (cacheIteratorsIterator != m_cacheIterators.end()) { + m_items.erase(descriptorsIterator->first); + m_cache.erase(cacheIteratorsIterator->second); + m_cacheIterators.erase(cacheIteratorsIterator); + } +} + +template std::pair::const_iterator, bool> SwappedMap::insert(const std::pair& value) { + uint64_t itemsFileSize; + + { + if (!m_itemsFile) { + throw std::runtime_error("SwappedMap::insert"); + } + + m_itemsFile.seekp(m_itemsFileSize); + try { + boost::archive::binary_oarchive archive(m_itemsFile); + archive & value.second; + } catch (std::exception&) { + throw std::runtime_error("SwappedMap::insert"); + } + + itemsFileSize = m_itemsFile.tellp(); + } + + { + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::insert"); + } + + m_indexesFile.seekp(sizeof(uint64_t) + (sizeof(bool) + sizeof(Key) + sizeof(uint32_t)) * m_descriptors.size()); + bool valid = true; + m_indexesFile.write(reinterpret_cast(&valid), sizeof valid); + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::insert"); + } + + m_indexesFile.write(reinterpret_cast(&value.first), sizeof value.first); + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::insert"); + } + + uint32_t itemSize = static_cast(itemsFileSize - m_itemsFileSize); + m_indexesFile.write(reinterpret_cast(&itemSize), sizeof itemSize); + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::insert"); + } + + m_indexesFile.seekp(0); + uint64_t count = m_descriptors.size() + 1; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::insert"); + } + } + + Descriptor descriptor = { m_itemsFileSize, m_descriptors.size() }; + auto descriptorsInsert = m_descriptors.insert(std::make_pair(value.first, descriptor)); + m_itemsFileSize = itemsFileSize; + + T* newItem = &prepare(value.first)->second; + *newItem = value.second; + return std::make_pair(const_iterator(this, descriptorsInsert.first), true); +} + +template std::pair* SwappedMap::prepare(const Key& key) { + if (m_items.size() == m_poolSize) { + typename std::list::iterator cacheIter = m_cache.begin(); + m_items.erase(*cacheIter); + m_cacheIterators.erase(*cacheIter); + m_cache.erase(cacheIter); + } + + std::pair::iterator, bool> itemInsert = m_items.insert(std::make_pair(key, T())); + typename std::list::iterator cacheIter = m_cache.insert(m_cache.end(), key); + m_cacheIterators.insert(std::make_pair(key, cacheIter)); + return &*itemInsert.first; +} + +template const std::pair* SwappedMap::load(const Key& key, uint64_t offset) { + auto itemIterator = m_items.find(key); + if (itemIterator != m_items.end()) { + auto cacheIteratorsIterator = m_cacheIterators.find(key); + if (cacheIteratorsIterator->second != --m_cache.end()) { + m_cache.splice(m_cache.end(), m_cache, cacheIteratorsIterator->second); + } + + ++m_cacheHits; + return &*itemIterator; + } + + typename std::unordered_map::iterator descriptorsIterator = m_descriptors.find(key); + if (descriptorsIterator == m_descriptors.end()) { + throw std::runtime_error("SwappedMap::load"); + } + + if (!m_itemsFile) { + throw std::runtime_error("SwappedMap::load"); + } + + m_itemsFile.seekg(descriptorsIterator->second.offset); + T tempItem; + try { + boost::archive::binary_iarchive archive(m_itemsFile); + archive & tempItem; + } catch (std::exception&) { + throw std::runtime_error("SwappedMap::load"); + } + + std::pair* item = prepare(key); + std::swap(tempItem, item->second); + ++m_cacheMisses; + return item; +} diff --git a/src/cryptonote_core/SwappedVector.cpp b/src/cryptonote_core/SwappedVector.cpp new file mode 100755 index 00000000..df4e5156 --- /dev/null +++ b/src/cryptonote_core/SwappedVector.cpp @@ -0,0 +1 @@ +#include "SwappedVector.h" diff --git a/src/cryptonote_core/SwappedVector.h b/src/cryptonote_core/SwappedVector.h new file mode 100755 index 00000000..7285c7fe --- /dev/null +++ b/src/cryptonote_core/SwappedVector.h @@ -0,0 +1,284 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +#include "serialization/binary_archive.h" + +template class SwappedVector { +public: + SwappedVector(); + //SwappedVector(const SwappedVector&) = delete; + ~SwappedVector(); + //SwappedVector& operator=(const SwappedVector&) = delete; + + bool open(const std::string& itemFileName, const std::string& indexFileName, size_t poolSize); + void close(); + + bool empty() const; + uint64_t size() const; + const T& operator[](uint64_t index); + const T& front(); + const T& back(); + void clear(); + void pop_back(); + void push_back(const T& item); + +private: + struct ItemEntry; + struct CacheEntry; + + struct ItemEntry { + public: + T item; + typename std::list::iterator cacheIter; + }; + + struct CacheEntry { + public: + typename std::map::iterator itemIter; + }; + + std::fstream m_itemsFile; + std::fstream m_indexesFile; + size_t m_poolSize; + std::vector m_offsets; + uint64_t m_itemsFileSize; + std::map m_items; + std::list m_cache; + uint64_t m_cacheHits; + uint64_t m_cacheMisses; + + T* prepare(uint64_t index); +}; + +template SwappedVector::SwappedVector() { +} + +template SwappedVector::~SwappedVector() { + close(); +} + +template bool SwappedVector::open(const std::string& itemFileName, const std::string& indexFileName, size_t poolSize) { + if (poolSize == 0) { + return false; + } + + m_itemsFile.open(itemFileName, std::ios::in | std::ios::out | std::ios::binary); + m_indexesFile.open(indexFileName, std::ios::in | std::ios::out | std::ios::binary); + if (m_itemsFile && m_indexesFile) { + uint64_t count; + m_indexesFile.read(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + return false; + } + + std::vector offsets; + uint64_t itemsFileSize = 0; + for (uint64_t i = 0; i < count; ++i) { + uint32_t itemSize; + m_indexesFile.read(reinterpret_cast(&itemSize), sizeof itemSize); + if (!m_indexesFile) { + return false; + } + + offsets.emplace_back(itemsFileSize); + itemsFileSize += itemSize; + } + + m_offsets.swap(offsets); + m_itemsFileSize = itemsFileSize; + } else { + m_itemsFile.open(itemFileName, std::ios::out | std::ios::binary); + m_itemsFile.close(); + m_itemsFile.open(itemFileName, std::ios::in | std::ios::out | std::ios::binary); + m_indexesFile.open(indexFileName, std::ios::out | std::ios::binary); + uint64_t count = 0; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + return false; + } + + m_indexesFile.close(); + m_indexesFile.open(indexFileName, std::ios::in | std::ios::out | std::ios::binary); + m_offsets.clear(); + m_itemsFileSize = 0; + } + + m_poolSize = poolSize; + m_items.clear(); + m_cache.clear(); + m_cacheHits = 0; + m_cacheMisses = 0; + return true; +} + +template void SwappedVector::close() { + std::cout << "SwappedVector cache hits: " << m_cacheHits << ", misses: " << m_cacheMisses << " (" << std::fixed << std::setprecision(2) << static_cast(m_cacheMisses) / (m_cacheHits + m_cacheMisses) * 100 << "%)" << std::endl; +} + +template bool SwappedVector::empty() const { + return m_offsets.empty(); +} + +template uint64_t SwappedVector::size() const { + return m_offsets.size(); +} + +template const T& SwappedVector::operator[](uint64_t index) { + auto itemIter = m_items.find(index); + if (itemIter != m_items.end()) { + if (itemIter->second.cacheIter != --m_cache.end()) { + m_cache.splice(m_cache.end(), m_cache, itemIter->second.cacheIter); + } + + ++m_cacheHits; + return itemIter->second.item; + } + + if (index >= m_offsets.size()) { + throw std::runtime_error("SwappedVector::operator[]"); + } + + if (!m_itemsFile) { + throw std::runtime_error("SwappedVector::operator[]"); + } + + m_itemsFile.seekg(m_offsets[index]); + T tempItem; + //try { + //boost::archive::binary_iarchive archive(m_itemsFile); + //archive & tempItem; + //} catch (std::exception&) { + // throw std::runtime_error("SwappedVector::operator[]"); + //} + + binary_archive archive(m_itemsFile); + if (!do_serialize(archive, tempItem)) { + throw std::runtime_error("SwappedVector::operator[]"); + } + + T* item = prepare(index); + std::swap(tempItem, *item); + ++m_cacheMisses; + return *item; +} + +template const T& SwappedVector::front() { + return operator[](0); +} + +template const T& SwappedVector::back() { + return operator[](m_offsets.size() - 1); +} + +template void SwappedVector::clear() { + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::clear"); + } + + m_indexesFile.seekp(0); + uint64_t count = 0; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::clear"); + } + + m_offsets.clear(); + m_itemsFileSize = 0; + m_items.clear(); + m_cache.clear(); +} + +template void SwappedVector::pop_back() { + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::pop_back"); + } + + m_indexesFile.seekp(0); + uint64_t count = m_offsets.size() - 1; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::pop_back"); + } + + m_itemsFileSize = m_offsets.back(); + m_offsets.pop_back(); + auto itemIter = m_items.find(m_offsets.size()); + if (itemIter != m_items.end()) { + m_cache.erase(itemIter->second.cacheIter); + m_items.erase(itemIter); + } +} + +template void SwappedVector::push_back(const T& item) { + uint64_t itemsFileSize; + + { + if (!m_itemsFile) { + throw std::runtime_error("SwappedVector::push_back"); + } + + m_itemsFile.seekp(m_itemsFileSize); + //try { + // boost::archive::binary_oarchive archive(m_itemsFile); + // archive & item; + //} catch (std::exception&) { + // throw std::runtime_error("SwappedVector::push_back"); + //} + + binary_archive archive(m_itemsFile); + if (!do_serialize(archive, *const_cast(&item))) { + throw std::runtime_error("SwappedVector::push_back"); + } + + itemsFileSize = m_itemsFile.tellp(); + } + + { + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::push_back"); + } + + m_indexesFile.seekp(sizeof(uint64_t) + sizeof(uint32_t) * m_offsets.size()); + uint32_t itemSize = static_cast(itemsFileSize - m_itemsFileSize); + m_indexesFile.write(reinterpret_cast(&itemSize), sizeof itemSize); + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::push_back"); + } + + m_indexesFile.seekp(0); + uint64_t count = m_offsets.size() + 1; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::push_back"); + } + } + + m_offsets.push_back(m_itemsFileSize); + m_itemsFileSize = itemsFileSize; + + T* newItem = prepare(m_offsets.size() - 1); + *newItem = item; +} + +template T* SwappedVector::prepare(uint64_t index) { + if (m_items.size() == m_poolSize) { + auto cacheIter = m_cache.begin(); + m_items.erase(cacheIter->itemIter); + m_cache.erase(cacheIter); + } + + auto itemIter = m_items.insert(std::make_pair(index, ItemEntry())); + CacheEntry cacheEntry = { itemIter.first }; + auto cacheIter = m_cache.insert(m_cache.end(), cacheEntry); + itemIter.first->second.cacheIter = cacheIter; + return &itemIter.first->second.item; +} diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 1787ec0a..f569e506 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -2,79 +2,269 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#include "blockchain_storage.h" + #include #include #include #include -#include "include_base_utils.h" -#include "cryptonote_basic_impl.h" -#include "blockchain_storage.h" #include "cryptonote_format_utils.h" #include "cryptonote_boost_serialization.h" -#include "blockchain_storage_boost_serialization.h" -#include "cryptonote_config.h" -#include "miner.h" -#include "misc_language.h" + #include "profile_tools.h" #include "file_io_utils.h" #include "common/boost_serialization_helper.h" -#include "warnings.h" -#include "crypto/hash.h" -//#include "serialization/json_archive.h" -using namespace std; -using namespace epee; +//namespace { +// std::string hashHex(const crypto::hash& hash) { +// std::string result; +// for (size_t i = 0; i < crypto::HASH_SIZE; ++i) { +// result += "0123456789ABCDEF"[static_cast(hash.data[i]) >> 4]; +// result += "0123456789ABCDEF"[static_cast(hash.data[i]) & 15]; +// } +// +// return result; +// } +//} + +namespace { + std::string appendPath(const std::string& path, const std::string& fileName) { + std::string result = path; + if (!result.empty()) { + result += '/'; + } + + result += fileName; + return result; + } +} + +namespace std { + bool operator<(const crypto::hash& hash1, const crypto::hash& hash2) { + return memcmp(&hash1, &hash2, crypto::HASH_SIZE) < 0; + } + + bool operator<(const crypto::key_image& keyImage1, const crypto::key_image& keyImage2) { + return memcmp(&keyImage1, &keyImage2, 32) < 0; + } +} + using namespace cryptonote; DISABLE_VS_WARNINGS(4267) -//------------------------------------------------------------------ -bool blockchain_storage::have_tx(const crypto::hash &id) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - return m_transactions.find(id) != m_transactions.end(); +namespace cryptonote { + struct transaction_chain_entry { + transaction tx; + uint64_t m_keeper_block_height; + size_t m_blob_size; + std::vector m_global_output_indexes; + + template void serialize(archive_t & ar, unsigned int version); + }; + + struct block_extended_info { + block bl; + uint64_t height; + size_t block_cumulative_size; + difficulty_type cumulative_difficulty; + uint64_t already_generated_coins; + + template void serialize(archive_t & ar, unsigned int version); + }; + + template void transaction_chain_entry::serialize(archive_t & ar, unsigned int version) { + ar & tx; + ar & m_keeper_block_height; + ar & m_blob_size; + ar & m_global_output_indexes; + } + + template void block_extended_info::serialize(archive_t & ar, unsigned int version) { + ar & bl; + ar & height; + ar & cumulative_difficulty; + ar & block_cumulative_size; + ar & already_generated_coins; + } } -//------------------------------------------------------------------ -bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) -{ + +template void cryptonote::blockchain_storage::Transaction::serialize(Archive& archive, unsigned int version) { + archive & tx; +} + +template void cryptonote::blockchain_storage::Block::serialize(Archive& archive, unsigned int version) { + archive & bl; + archive & height; + archive & block_cumulative_size; + archive & cumulative_difficulty; + archive & already_generated_coins; + archive & transactions; +} + +template void cryptonote::blockchain_storage::TransactionIndex::serialize(Archive& archive, unsigned int version) { + archive & block; + archive & transaction; +} + +namespace cryptonote { +#define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 13 + + template void blockchain_storage::serialize(archive_t & ar, const unsigned int version) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if (version < 12) { + LOG_PRINT_L0("Detected blockchain of unsupported version, migration is not possible."); + return; + } + + LOG_PRINT_L0("Blockchain of previous version detected, migrating. This may take several minutes, please be patient..."); + + std::vector blocks; + ar & blocks; + + { + std::unordered_map blocks_index; + ar & blocks_index; + } + + std::unordered_map transactions; + ar & transactions; + + { + std::unordered_set spent_keys; + ar & spent_keys; + } + + { + std::unordered_map alternative_chains; + ar & alternative_chains; + } + + { + std::map>> outputs; + ar & outputs; + } + + { + std::unordered_map invalid_blocks; + ar & invalid_blocks; + } + + size_t current_block_cumul_sz_limit; + ar & current_block_cumul_sz_limit; + LOG_PRINT_L0("Old blockchain storage:" << ENDL << + "blocks: " << blocks.size() << ENDL << + "transactions: " << transactions.size() << ENDL << + "current_block_cumul_sz_limit: " << current_block_cumul_sz_limit); + + Block block; + Transaction transaction; + for (uint32_t b = 0; b < blocks.size(); ++b) { + block.bl = blocks[b].bl; + block.height = b; + block.block_cumulative_size = blocks[b].block_cumulative_size; + block.cumulative_difficulty = blocks[b].cumulative_difficulty; + block.already_generated_coins = blocks[b].already_generated_coins; + block.transactions.resize(1 + blocks[b].bl.tx_hashes.size()); + block.transactions[0].tx = blocks[b].bl.miner_tx; + TransactionIndex transactionIndex = { b, 0 }; + pushTransaction(block, get_transaction_hash(blocks[b].bl.miner_tx), transactionIndex); + for (uint32_t t = 0; t < blocks[b].bl.tx_hashes.size(); ++t) { + block.transactions[1 + t].tx = transactions[blocks[b].bl.tx_hashes[t]].tx; + transactionIndex.transaction = 1 + t; + pushTransaction(block, blocks[b].bl.tx_hashes[t], transactionIndex); + } + + pushBlock(block); + } + + update_next_comulative_size_limit(); + if (m_current_block_cumul_sz_limit != current_block_cumul_sz_limit) { + LOG_ERROR("Migration was unsuccessful."); + } + } +} + +BOOST_CLASS_VERSION(cryptonote::blockchain_storage, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) + + +bool blockchain_storage::have_tx(const crypto::hash &id) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_transactionMap.find(id) != m_transactionMap.end(); +} + +bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) { CRITICAL_REGION_LOCAL(m_blockchain_lock); return m_spent_keys.find(key_im) != m_spent_keys.end(); } -//------------------------------------------------------------------ -transaction *blockchain_storage::get_tx(const crypto::hash &id) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto it = m_transactions.find(id); - if (it == m_transactions.end()) - return NULL; - return &it->second.tx; -} -//------------------------------------------------------------------ -uint64_t blockchain_storage::get_current_blockchain_height() -{ +uint64_t blockchain_storage::get_current_blockchain_height() { CRITICAL_REGION_LOCAL(m_blockchain_lock); return m_blocks.size(); } -//------------------------------------------------------------------ -bool blockchain_storage::init(const std::string& config_folder) -{ + +bool blockchain_storage::init(const std::string& config_folder) { CRITICAL_REGION_LOCAL(m_blockchain_lock); m_config_folder = config_folder; LOG_PRINT_L0("Loading blockchain..."); - const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME; - if(!tools::unserialize_obj_from_file(*this, filename)) - { - LOG_PRINT_L0("Can't load blockchain storage from file, generating genesis block."); - block bl = boost::value_initialized(); - block_verification_context bvc = boost::value_initialized(); - generate_genesis_block(bl); - add_new_block(bl, bvc); - CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed && bvc.m_added_to_main_chain, false, "Failed to add genesis block to blockchain"); + if (!m_blocks.open(appendPath(config_folder, "blocks.dat"), appendPath(config_folder, "blockindexes.dat"), 1024)) { + return false; } - if(!m_blocks.size()) - { + + if (m_blocks.empty()) { + const std::string filename = appendPath(m_config_folder, CRYPTONOTE_BLOCKCHAINDATA_FILENAME); + if (!tools::unserialize_obj_from_file(*this, filename)) { + LOG_PRINT_L0("Can't load blockchain storage from file."); + } + } else { + bool rebuild = true; + try { + std::ifstream file(appendPath(config_folder, "blockscache.dat"), std::ios::binary); + boost::archive::binary_iarchive archive(file); + crypto::hash lastBlockHash; + archive & lastBlockHash; + if (lastBlockHash == get_block_hash(m_blocks.back().bl)) { + archive & m_blockMap; + archive & m_transactionMap; + archive & m_spent_keys; + archive & m_outputs; + rebuild = false; + } + } catch (std::exception&) { + } + + if (rebuild) { + LOG_PRINT_L0("No actual blockchain cache found, rebuilding internal structures..."); + std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); + for (uint32_t b = 0; b < m_blocks.size(); ++b) { + const Block& block = m_blocks[b]; + crypto::hash blockHash = get_block_hash(block.bl); + m_blockMap.insert(std::make_pair(blockHash, b)); + for (uint16_t t = 0; t < block.transactions.size(); ++t) { + const Transaction& transaction = block.transactions[t]; + crypto::hash transactionHash = get_transaction_hash(transaction.tx); + TransactionIndex transactionIndex = { b, t }; + m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); + for (auto& i : transaction.tx.vin) { + if (i.type() == typeid(txin_to_key)) { + m_spent_keys.insert(::boost::get(i).k_image); + } + } + + for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) { + m_outputs[transaction.tx.vout[o].amount].push_back(std::make_pair<>(transactionIndex, o)); + } + } + } + + std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; + LOG_PRINT_L0("Rebuilding internal structures took: " << duration.count()); + } + } + + if (m_blocks.empty()) { LOG_PRINT_L0("Blockchain not loaded, generating genesis block."); block bl = boost::value_initialized(); block_verification_context bvc = boost::value_initialized(); @@ -82,78 +272,107 @@ bool blockchain_storage::init(const std::string& config_folder) add_new_block(bl, bvc); CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain"); } + uint64_t timestamp_diff = time(NULL) - m_blocks.back().bl.timestamp; - if(!m_blocks.back().bl.timestamp) + if (!m_blocks.back().bl.timestamp) timestamp_diff = time(NULL) - 1341378000; - LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_blocks.size()-1 << ", " << misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0); + LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_blocks.size() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0); return true; } -//------------------------------------------------------------------ -bool blockchain_storage::store_blockchain() -{ - m_is_blockchain_storing = true; - misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([&](){m_is_blockchain_storing=false;}); - LOG_PRINT_L0("Storing blockchain..."); - if (!tools::create_directories_if_necessary(m_config_folder)) - { - LOG_PRINT_L0("Failed to create data directory: " << m_config_folder); - return false; +bool blockchain_storage::store_blockchain() { + try { + std::ofstream file(appendPath(m_config_folder, "blockscache.dat"), std::ios::binary); + boost::archive::binary_oarchive archive(file); + crypto::hash lastBlockHash = get_block_hash(m_blocks.back().bl); + archive & lastBlockHash; + archive & m_blockMap; + archive & m_transactionMap; + archive & m_spent_keys; + archive & m_outputs; + } catch (std::exception& e) { + LOG_ERROR("Failed to save blockchain, " << e.what()); } - const std::string temp_filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME; - // There is a chance that temp_filename and filename are hardlinks to the same file - std::remove(temp_filename.c_str()); - if(!tools::serialize_obj_to_file(*this, temp_filename)) + //{ + // std::ofstream file(appendPath(m_config_folder, "blockscache2.dat"), std::ios::binary); + + // crypto::hash lastBlockHash = get_block_hash(m_blocks.back().bl); + // file.write(reinterpret_cast(&lastBlockHash), sizeof(lastBlockHash)); + + // uint32_t blockMapSize = m_blockMap.size(); + // file.write(reinterpret_cast(&blockMapSize), sizeof(blockMapSize)); + // for (auto& i : m_blockMap) { + // crypto::hash blockHash = i.first; + // file.write(reinterpret_cast(&blockHash), sizeof(blockHash)); + + // uint32_t blockIndex = i.second; + // file.write(reinterpret_cast(&blockIndex), sizeof(blockIndex)); + // } + + // uint32_t transactionMapSize = m_transactionMap.size(); + // file.write(reinterpret_cast(&transactionMapSize), sizeof(transactionMapSize)); + // for (auto& i : m_transactionMap) { + // crypto::hash transactionHash = i.first; + // file.write(reinterpret_cast(&transactionHash), sizeof(transactionHash)); + + // uint32_t blockIndex = i.second.block; + // file.write(reinterpret_cast(&blockIndex), sizeof(blockIndex)); + + // uint32_t transactionIndex = i.second.transaction; + // file.write(reinterpret_cast(&transactionIndex), sizeof(transactionIndex)); + // } + + // uint32_t spentKeysSize = m_spent_keys.size(); + // file.write(reinterpret_cast(&spentKeysSize), sizeof(spentKeysSize)); + // for (auto& i : m_spent_keys) { + // crypto::key_image key = i; + // file.write(reinterpret_cast(&key), sizeof(key)); + // } + + // uint32_t outputsSize = m_outputs.size(); + // file.write(reinterpret_cast(&outputsSize), sizeof(outputsSize)); + // for (auto& i : m_outputs) { + // uint32_t indexesSize = i.second.size(); + // file.write(reinterpret_cast(&indexesSize), sizeof(indexesSize)); + // for (auto& j : i.second) { + // uint32_t blockIndex = j.first.block; + // file.write(reinterpret_cast(&blockIndex), sizeof(blockIndex)); + + // uint32_t transactionIndex = j.first.transaction; + // file.write(reinterpret_cast(&transactionIndex), sizeof(transactionIndex)); + + // uint32_t outputIndex = j.second; + // file.write(reinterpret_cast(&outputIndex), sizeof(outputIndex)); + // } + // } + //} + { - //achtung! - LOG_ERROR("Failed to save blockchain data to file: " << temp_filename); - return false; + //std::ofstream file(appendPath(m_config_folder, "blockscache3.dat"), std::ios::binary); + //binary_archive archive(file); + //crypto::hash lastBlockHash = get_block_hash(m_blocks.back().bl); + //do_serialize(archive, lastBlockHash); + //do_serialize(archive, m_blockMap); + //do_serialize(archive, m_transactionMap); + //do_serialize(archive, m_spent_keys); + //do_serialize(archive, m_outputs); } - const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME; - std::error_code ec = tools::replace_file(temp_filename, filename); - if (ec) - { - LOG_ERROR("Failed to rename blockchain data file " << temp_filename << " to " << filename << ": " << ec.message() << ':' << ec.value()); - return false; - } - LOG_PRINT_L0("Blockchain stored OK."); + return true; } -//------------------------------------------------------------------ -bool blockchain_storage::deinit() -{ + +bool blockchain_storage::deinit() { return store_blockchain(); } -//------------------------------------------------------------------ -bool blockchain_storage::pop_block_from_blockchain() -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - CHECK_AND_ASSERT_MES(m_blocks.size() > 1, false, "pop_block_from_blockchain: can't pop from blockchain with size = " << m_blocks.size()); - size_t h = m_blocks.size()-1; - block_extended_info& bei = m_blocks[h]; - //crypto::hash id = get_block_hash(bei.bl); - bool r = purge_block_data_from_blockchain(bei.bl, bei.bl.tx_hashes.size()); - CHECK_AND_ASSERT_MES(r, false, "Failed to purge_block_data_from_blockchain for block " << get_block_hash(bei.bl) << " on height " << h); - - //remove from index - auto bl_ind = m_blocks_index.find(get_block_hash(bei.bl)); - CHECK_AND_ASSERT_MES(bl_ind != m_blocks_index.end(), false, "pop_block_from_blockchain: blockchain id not found in index"); - m_blocks_index.erase(bl_ind); - //pop block from core - m_blocks.pop_back(); - m_tx_pool.on_blockchain_dec(m_blocks.size()-1, get_tail_id()); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::reset_and_set_genesis_block(const block& b) -{ +bool blockchain_storage::reset_and_set_genesis_block(const block& b) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - m_transactions.clear(); - m_spent_keys.clear(); m_blocks.clear(); - m_blocks_index.clear(); + m_blockMap.clear(); + m_transactionMap.clear(); + + m_spent_keys.clear(); m_alternative_chains.clear(); m_outputs.clear(); @@ -161,219 +380,116 @@ bool blockchain_storage::reset_and_set_genesis_block(const block& b) add_new_block(b, bvc); return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; } -//------------------------------------------------------------------ -bool blockchain_storage::purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check) -{ + +crypto::hash blockchain_storage::get_tail_id(uint64_t& height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - struct purge_transaction_visitor: public boost::static_visitor - { - key_images_container& m_spent_keys; - bool m_strict_check; - purge_transaction_visitor(key_images_container& spent_keys, bool strict_check):m_spent_keys(spent_keys), m_strict_check(strict_check){} - - bool operator()(const txin_to_key& inp) const - { - //const crypto::key_image& ki = inp.k_image; - auto r = m_spent_keys.find(inp.k_image); - if(r != m_spent_keys.end()) - { - m_spent_keys.erase(r); - }else - { - CHECK_AND_ASSERT_MES(!m_strict_check, false, "purge_block_data_from_blockchain: key image in transaction not found"); - } - return true; - } - bool operator()(const txin_gen& inp) const - { - return true; - } - bool operator()(const txin_to_script& tx) const - { - return false; - } - - bool operator()(const txin_to_scripthash& tx) const - { - return false; - } - }; - - BOOST_FOREACH(const txin_v& in, tx.vin) - { - bool r = boost::apply_visitor(purge_transaction_visitor(m_spent_keys, strict_check), in); - CHECK_AND_ASSERT_MES(!strict_check || r, false, "failed to process purge_transaction_visitor"); - } - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& tx_id) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto tx_index_it = m_transactions.find(tx_id); - CHECK_AND_ASSERT_MES(tx_index_it != m_transactions.end(), false, "purge_block_data_from_blockchain: transaction not found in blockchain index!!"); - transaction& tx = tx_index_it->second.tx; - - purge_transaction_keyimages_from_blockchain(tx, true); - - if(!is_coinbase(tx)) - { - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - bool r = m_tx_pool.add_tx(tx, tvc, true); - CHECK_AND_ASSERT_MES(r, false, "purge_block_data_from_blockchain: failed to add transaction to transaction pool"); - } - - bool res = pop_transaction_from_global_index(tx, tx_id); - m_transactions.erase(tx_index_it); - LOG_PRINT_L1("Removed transaction from blockchain history:" << tx_id << ENDL); - return res; -} -//------------------------------------------------------------------ -bool blockchain_storage::purge_block_data_from_blockchain(const block& bl, size_t processed_tx_count) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - bool res = true; - CHECK_AND_ASSERT_MES(processed_tx_count <= bl.tx_hashes.size(), false, "wrong processed_tx_count in purge_block_data_from_blockchain"); - for(size_t count = 0; count != processed_tx_count; count++) - { - res = purge_transaction_from_blockchain(bl.tx_hashes[(processed_tx_count -1)- count]) && res; - } - - res = purge_transaction_from_blockchain(get_transaction_hash(bl.miner_tx)) && res; - - return res; -} -//------------------------------------------------------------------ -crypto::hash blockchain_storage::get_tail_id(uint64_t& height) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - height = get_current_blockchain_height()-1; + height = get_current_blockchain_height() - 1; return get_tail_id(); } -//------------------------------------------------------------------ -crypto::hash blockchain_storage::get_tail_id() -{ + +crypto::hash blockchain_storage::get_tail_id() { CRITICAL_REGION_LOCAL(m_blockchain_lock); crypto::hash id = null_hash; - if(m_blocks.size()) - { + if (m_blocks.size()) { get_block_hash(m_blocks.back().bl, id); } + return id; } -//------------------------------------------------------------------ -bool blockchain_storage::get_short_chain_history(std::list& ids) -{ + +bool blockchain_storage::get_short_chain_history(std::list& ids) { CRITICAL_REGION_LOCAL(m_blockchain_lock); size_t i = 0; size_t current_multiplier = 1; size_t sz = m_blocks.size(); - if(!sz) + if (!sz) return true; size_t current_back_offset = 1; bool genesis_included = false; - while(current_back_offset < sz) + while (current_back_offset < sz) { - ids.push_back(get_block_hash(m_blocks[sz-current_back_offset].bl)); - if(sz-current_back_offset == 0) + ids.push_back(get_block_hash(m_blocks[sz - current_back_offset].bl)); + if (sz - current_back_offset == 0) genesis_included = true; - if(i < 10) + if (i < 10) { ++current_back_offset; - }else + } else { current_back_offset += current_multiplier *= 2; } ++i; } - if(!genesis_included) + if (!genesis_included) ids.push_back(get_block_hash(m_blocks[0].bl)); return true; } -//------------------------------------------------------------------ -crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) -{ + +crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(height >= m_blocks.size()) + if (height >= m_blocks.size()) return null_hash; return get_block_hash(m_blocks[height].bl); } -//------------------------------------------------------------------ -bool blockchain_storage::get_block_by_hash(const crypto::hash &h, block &blk) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - // try to find block in main chain - blocks_by_id_index::const_iterator it = m_blocks_index.find(h); - if (m_blocks_index.end() != it) { - blk = m_blocks[it->second].bl; +bool blockchain_storage::get_block_by_hash(const crypto::hash& blockHash, block& b) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + auto blockIndexByHashIterator = m_blockMap.find(blockHash); + if (blockIndexByHashIterator != m_blockMap.end()) { + b = m_blocks[blockIndexByHashIterator->second].bl; return true; } - // try to find block in alternative chain - blocks_ext_by_hash::const_iterator it_alt = m_alternative_chains.find(h); - if (m_alternative_chains.end() != it_alt) { - blk = it_alt->second.bl; + auto blockByHashIterator = m_alternative_chains.find(blockHash); + if (blockByHashIterator != m_alternative_chains.end()) { + b = blockByHashIterator->second.bl; return true; } return false; } -//------------------------------------------------------------------ -void blockchain_storage::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(blocks_by_id_index::value_type &v, m_blocks_index) - main.push_back(v.first); - - BOOST_FOREACH(blocks_ext_by_hash::value_type &v, m_alternative_chains) - alt.push_back(v.first); - - BOOST_FOREACH(blocks_ext_by_hash::value_type &v, m_invalid_blocks) - invalid.push_back(v.first); -} -//------------------------------------------------------------------ -difficulty_type blockchain_storage::get_difficulty_for_next_block() -{ +difficulty_type blockchain_storage::get_difficulty_for_next_block() { CRITICAL_REGION_LOCAL(m_blockchain_lock); std::vector timestamps; std::vector commulative_difficulties; - size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast(DIFFICULTY_BLOCKS_COUNT)); - if(!offset) - ++offset;//skip genesis block - for(; offset < m_blocks.size(); offset++) - { + size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast(DIFFICULTY_BLOCKS_COUNT)); + if (offset == 0) { + ++offset; + } + + for (; offset < m_blocks.size(); offset++) { timestamps.push_back(m_blocks[offset].bl.timestamp); commulative_difficulties.push_back(m_blocks[offset].cumulative_difficulty); } + return next_difficulty(timestamps, commulative_difficulties); } -//------------------------------------------------------------------ -bool blockchain_storage::rollback_blockchain_switching(std::list& original_chain, size_t rollback_height) -{ + +bool blockchain_storage::rollback_blockchain_switching(std::list& original_chain, size_t rollback_height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); //remove failed subchain - for(size_t i = m_blocks.size()-1; i >=rollback_height; i--) + for (size_t i = m_blocks.size() - 1; i >= rollback_height; i--) { - bool r = pop_block_from_blockchain(); - CHECK_AND_ASSERT_MES(r, false, "PANIC!!! failed to remove block while chain switching during the rollback!"); + popBlock(get_block_hash(m_blocks.back().bl)); + //bool r = pop_block_from_blockchain(); + //CHECK_AND_ASSERT_MES(r, false, "PANIC!!! failed to remove block while chain switching during the rollback!"); } //return back original chain BOOST_FOREACH(auto& bl, original_chain) { block_verification_context bvc = boost::value_initialized(); - bool r = handle_block_to_main_chain(bl, bvc); + bool r = pushBlock(bl, bvc); CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC!!! failed to add (again) block while chain switching during the rollback!"); } LOG_PRINT_L0("Rollback success."); return true; } -//------------------------------------------------------------------ -bool blockchain_storage::switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain) -{ + +bool blockchain_storage::switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain) { CRITICAL_REGION_LOCAL(m_blockchain_lock); CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed"); @@ -382,47 +498,41 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list disconnected_chain; - for(size_t i = m_blocks.size()-1; i >=split_height; i--) - { + for (size_t i = m_blocks.size() - 1; i >= split_height; i--) { block b = m_blocks[i].bl; - bool r = pop_block_from_blockchain(); - CHECK_AND_ASSERT_MES(r, false, "failed to remove block on chain switching"); + popBlock(get_block_hash(b)); + //CHECK_AND_ASSERT_MES(r, false, "failed to remove block on chain switching"); disconnected_chain.push_front(b); } //connecting new alternative chain - for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++) - { + for (auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++) { auto ch_ent = *alt_ch_iter; block_verification_context bvc = boost::value_initialized(); - bool r = handle_block_to_main_chain(ch_ent->second.bl, bvc); - if(!r || !bvc.m_added_to_main_chain) - { + bool r = pushBlock(ch_ent->second.bl, bvc); + if (!r || !bvc.m_added_to_main_chain) { LOG_PRINT_L0("Failed to switch to alternative blockchain"); rollback_blockchain_switching(disconnected_chain, split_height); - add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl)); + //add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl)); LOG_PRINT_L0("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl)); m_alternative_chains.erase(ch_ent); - for(auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++) - { + for (auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++) { //block_verification_context bvc = boost::value_initialized(); - add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first); + //add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first); m_alternative_chains.erase(*alt_ch_to_orph_iter); } + return false; } } - if(!discard_disconnected_chain) - { + if (!discard_disconnected_chain) { //pushing old chain as alternative chain - BOOST_FOREACH(auto& old_ch_ent, disconnected_chain) - { + for (auto& old_ch_ent : disconnected_chain) { block_verification_context bvc = boost::value_initialized(); bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc); - if(!r) - { + if (!r) { LOG_ERROR("Failed to push ex-main chain blocks to alternative chain "); rollback_blockchain_switching(disconnected_chain, split_height); return false; @@ -431,138 +541,127 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list& alt_chain, block_extended_info& bei) -{ + +difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list& alt_chain, Block& bei) { std::vector timestamps; std::vector commulative_difficulties; - if(alt_chain.size()< DIFFICULTY_BLOCKS_COUNT) - { + if (alt_chain.size() < DIFFICULTY_BLOCKS_COUNT) { CRITICAL_REGION_LOCAL(m_blockchain_lock); size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height; size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast(DIFFICULTY_BLOCKS_COUNT), alt_chain.size()); 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; - if(!main_chain_start_offset) + if (!main_chain_start_offset) ++main_chain_start_offset; //skip genesis block - for(; main_chain_start_offset < main_chain_stop_offset; ++main_chain_start_offset) - { + for (; main_chain_start_offset < main_chain_stop_offset; ++main_chain_start_offset) { timestamps.push_back(m_blocks[main_chain_start_offset].bl.timestamp); commulative_difficulties.push_back(m_blocks[main_chain_start_offset].cumulative_difficulty); } - CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()["<< alt_chain.size() - << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT ); - BOOST_FOREACH(auto it, alt_chain) - { + CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()[" << alt_chain.size() + << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT); + for (auto it : alt_chain) { timestamps.push_back(it->second.bl.timestamp); commulative_difficulties.push_back(it->second.cumulative_difficulty); } - }else - { + } else { timestamps.resize(std::min(alt_chain.size(), static_cast(DIFFICULTY_BLOCKS_COUNT))); commulative_difficulties.resize(std::min(alt_chain.size(), static_cast(DIFFICULTY_BLOCKS_COUNT))); size_t count = 0; - size_t max_i = timestamps.size()-1; - BOOST_REVERSE_FOREACH(auto it, alt_chain) - { + size_t max_i = timestamps.size() - 1; + BOOST_REVERSE_FOREACH(auto it, alt_chain) { timestamps[max_i - count] = it->second.bl.timestamp; commulative_difficulties[max_i - count] = it->second.cumulative_difficulty; count++; - if(count >= DIFFICULTY_BLOCKS_COUNT) + if (count >= DIFFICULTY_BLOCKS_COUNT) { break; + } } } + return next_difficulty(timestamps, commulative_difficulties); } -//------------------------------------------------------------------ -bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height) -{ + +bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height) { CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs"); CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type"); - if(boost::get(b.miner_tx.vin[0]).height != height) - { + if (boost::get(b.miner_tx.vin[0]).height != height) { LOG_PRINT_RED_L0("The miner transaction in block has invalid height: " << boost::get(b.miner_tx.vin[0]).height << ", expected: " << height); return false; } - CHECK_AND_ASSERT_MES(b.miner_tx.unlock_time == height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, - false, - "coinbase transaction transaction have wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); - //check outs overflow - if(!check_outs_overflow(b.miner_tx)) - { + CHECK_AND_ASSERT_MES(b.miner_tx.unlock_time == height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, + false, + "coinbase transaction transaction have wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + + if (!check_outs_overflow(b.miner_tx)) { LOG_PRINT_RED_L0("miner transaction have money overflow in block " << get_block_hash(b)); return false; } return true; } -//------------------------------------------------------------------ -bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) -{ - //validate reward + +bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) { uint64_t money_in_use = 0; - BOOST_FOREACH(auto& o, b.miner_tx.vout) + for (auto& o : b.miner_tx.vout) { money_in_use += o.amount; + } std::vector last_blocks_sizes; get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - if(!get_block_reward(misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward)) - { + if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward)) { LOG_PRINT_L0("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); return false; } - if(base_reward + fee < money_in_use) - { + + if (base_reward + fee < money_in_use) { LOG_ERROR("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); return false; } - if(base_reward + fee != money_in_use) - { + + if (base_reward + fee != money_in_use) { LOG_ERROR("coinbase transaction doesn't use full amount of block reward: spent: " - << print_money(money_in_use) << ", block reward " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); + << print_money(money_in_use) << ", block reward " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); return false; } + return true; } -//------------------------------------------------------------------ -bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count) -{ + +bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count) { CRITICAL_REGION_LOCAL(m_blockchain_lock); CHECK_AND_ASSERT_MES(from_height < m_blocks.size(), false, "Internal error: get_backward_blocks_sizes called with from_height=" << from_height << ", blockchain height = " << m_blocks.size()); - - size_t start_offset = (from_height+1) - std::min((from_height+1), count); - for(size_t i = start_offset; i != from_height+1; i++) + size_t start_offset = (from_height + 1) - std::min((from_height + 1), count); + for (size_t i = start_offset; i != from_height + 1; i++) { sz.push_back(m_blocks[i].block_cumulative_size); + } return true; } -//------------------------------------------------------------------ -bool blockchain_storage::get_last_n_blocks_sizes(std::vector& sz, size_t count) -{ + +bool blockchain_storage::get_last_n_blocks_sizes(std::vector& sz, size_t count) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!m_blocks.size()) + if (!m_blocks.size()) { return true; - return get_backward_blocks_sizes(m_blocks.size() -1, sz, count); + } + + return get_backward_blocks_sizes(m_blocks.size() - 1, sz, count); } -//------------------------------------------------------------------ -uint64_t blockchain_storage::get_current_comulative_blocksize_limit() -{ + +uint64_t blockchain_storage::get_current_comulative_blocksize_limit() { return m_current_block_cumul_sz_limit; } -//------------------------------------------------------------------ -bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) -{ + +bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) { size_t median_size; uint64_t already_generated_coins; @@ -585,6 +684,7 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee)) { return false; } + #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) size_t real_txs_size = 0; uint64_t real_fee = 0; @@ -625,7 +725,7 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad /* two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size - */ + */ //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11); CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance"); @@ -662,7 +762,7 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1); if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) { //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size - LOG_PRINT_RED("Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1 , LOG_LEVEL_2); + LOG_PRINT_RED("Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1, LOG_LEVEL_2); cumulative_size += delta - 1; continue; } @@ -676,43 +776,40 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad #endif return true; } + LOG_ERROR("Failed to create_block_template with " << 10 << " tries"); return false; } -//------------------------------------------------------------------ -bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector& timestamps) -{ - if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) +bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector& timestamps) { + if (timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) return true; CRITICAL_REGION_LOCAL(m_blockchain_lock); size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size(); CHECK_AND_ASSERT_MES(start_top_height < m_blocks.size(), false, "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size()); - size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements:0; + size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0; do { timestamps.push_back(m_blocks[start_top_height].bl.timestamp); - if(start_top_height == 0) + if (start_top_height == 0) break; --start_top_height; - }while(start_top_height != stop_offset); + } while (start_top_height != stop_offset); return true; } -//------------------------------------------------------------------ -bool blockchain_storage::handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc) -{ + +bool blockchain_storage::handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc) { CRITICAL_REGION_LOCAL(m_blockchain_lock); uint64_t block_height = get_block_height(b); - if(0 == block_height) - { - LOG_ERROR("Block with id: " << string_tools::pod_to_hex(id) << " (as alternative) have wrong miner transaction"); + if (block_height == 0) { + LOG_ERROR("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) have wrong miner transaction"); bvc.m_verifivation_failed = true; return false; } - if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) - { + + if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) { LOG_PRINT_RED_L0("Block with id: " << id << ENDL << " can't be accepted for alternative chain, block height: " << block_height << ENDL << " blockchain height: " << get_current_blockchain_height()); @@ -722,39 +819,35 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: //block is not related with head of main chain //first of all - look in alternative chains container - auto it_main_prev = m_blocks_index.find(b.prev_id); + auto it_main_prev = m_blockMap.find(b.prev_id); auto it_prev = m_alternative_chains.find(b.prev_id); - if(it_prev != m_alternative_chains.end() || it_main_prev != m_blocks_index.end()) - { + if (it_prev != m_alternative_chains.end() || it_main_prev != m_blockMap.end()) { //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 alt_chain; std::vector timestamps; - while(alt_it != m_alternative_chains.end()) - { + while (alt_it != m_alternative_chains.end()) { alt_chain.push_front(alt_it); timestamps.push_back(alt_it->second.bl.timestamp); alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id); } - if(alt_chain.size()) - { + if (alt_chain.size()) { //make sure that it has right connection to main chain CHECK_AND_ASSERT_MES(m_blocks.size() > alt_chain.front()->second.height, false, "main blockchain wrong height"); crypto::hash h = null_hash; get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h); CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain have wrong connection to main chain"); complete_timestamps_vector(alt_chain.front()->second.height - 1, timestamps); - }else - { - CHECK_AND_ASSERT_MES(it_main_prev != m_blocks_index.end(), false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"); + } else { + CHECK_AND_ASSERT_MES(it_main_prev != m_blockMap.end(), false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"); complete_timestamps_vector(it_main_prev->second, timestamps); } + //check timestamp correct - if(!check_block_timestamp(timestamps, b)) - { + if (!check_block_timestamp(timestamps, b)) { LOG_PRINT_RED_L0("Block with id: " << id << ENDL << " for alternative chain, have invalid timestamp: " << b.timestamp); //add_block_as_invalid(b, id);//do not add blocks to invalid storage before proof of work check was passed @@ -762,13 +855,12 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: return false; } - block_extended_info bei = boost::value_initialized(); + Block bei = boost::value_initialized(); bei.bl = b; bei.height = alt_chain.size() ? it_prev->second.height + 1 : it_main_prev->second + 1; bool is_a_checkpoint; - if(!m_checkpoints.check_block(bei.height, id, is_a_checkpoint)) - { + if (!m_checkpoints.check_block(bei.height, id, is_a_checkpoint)) { LOG_ERROR("CHECKPOINT VALIDATION FAILED"); bvc.m_verifivation_failed = true; return false; @@ -780,8 +872,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"); crypto::hash proof_of_work = null_hash; get_block_longhash(bei.bl, proof_of_work, bei.height); - if(!check_hash(proof_of_work, current_diff)) - { + if (!check_hash(proof_of_work, current_diff)) { LOG_PRINT_RED_L0("Block with id: " << id << ENDL << " for alternative chain, have not enough proof of work: " << proof_of_work << ENDL << " expected difficulty: " << current_diff); @@ -789,54 +880,49 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: return false; } - if(!prevalidate_miner_transaction(b, bei.height)) - { - LOG_PRINT_RED_L0("Block with id: " << string_tools::pod_to_hex(id) - << " (as alternative) have wrong miner transaction."); + if (!prevalidate_miner_transaction(b, bei.height)) { + LOG_PRINT_RED_L0("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) have wrong miner transaction."); bvc.m_verifivation_failed = true; return false; - } - bei.cumulative_difficulty = alt_chain.size() ? it_prev->second.cumulative_difficulty: m_blocks[it_main_prev->second].cumulative_difficulty; + bei.cumulative_difficulty = alt_chain.size() ? it_prev->second.cumulative_difficulty : m_blocks[it_main_prev->second].cumulative_difficulty; bei.cumulative_difficulty += current_diff; #ifdef _DEBUG auto i_dres = m_alternative_chains.find(id); CHECK_AND_ASSERT_MES(i_dres == m_alternative_chains.end(), false, "insertion of new alternative block returned as it already exist"); #endif + auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei)); CHECK_AND_ASSERT_MES(i_res.second, false, "insertion of new alternative block returned as it already exist"); alt_chain.push_back(i_res.first); - if(is_a_checkpoint) - { + if (is_a_checkpoint) { //do reorganize! LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << ", checkpoint is found in alternative chain on height " << bei.height, LOG_LEVEL_0); bool r = switch_to_alternative_blockchain(alt_chain, true); - if(r) bvc.m_added_to_main_chain = true; + if (r) bvc.m_added_to_main_chain = true; else bvc.m_verifivation_failed = true; return r; - }else if(m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain + } else if (m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain { //do reorganize! LOG_PRINT_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, LOG_LEVEL_0); bool r = switch_to_alternative_blockchain(alt_chain, false); - if(r) bvc.m_added_to_main_chain = true; + if (r) bvc.m_added_to_main_chain = true; else bvc.m_verifivation_failed = true; return r; - }else - { + } else { LOG_PRINT_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height << ENDL << "id:\t" << id << ENDL << "PoW:\t" << proof_of_work << ENDL << "difficulty:\t" << current_diff, LOG_LEVEL_0); return true; } - }else - { + } else { //block orphaned bvc.m_marked_as_orphaned = true; LOG_PRINT_RED_L0("Block recognized as orphaned and rejected, id = " << id); @@ -844,13 +930,12 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: return true; } -//------------------------------------------------------------------ -bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) -{ + +bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(start_offset >= m_blocks.size()) + if (start_offset >= m_blocks.size()) return false; - for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++) + for (size_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) { blocks.push_back(m_blocks[i].bl); std::list missed_ids; @@ -860,81 +945,75 @@ bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::li return true; } -//------------------------------------------------------------------ -bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(start_offset >= m_blocks.size()) - return false; - for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++) +bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if (start_offset >= m_blocks.size()) { + return false; + } + + for (size_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) { blocks.push_back(m_blocks[i].bl); + } + return true; } -//------------------------------------------------------------------ -bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) -{ + +bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { CRITICAL_REGION_LOCAL(m_blockchain_lock); rsp.current_blockchain_height = get_current_blockchain_height(); std::list blocks; get_blocks(arg.blocks, blocks, rsp.missed_ids); - BOOST_FOREACH(const auto& bl, blocks) - { + for (const auto& bl : blocks) { std::list missed_tx_id; std::list txs; get_transactions(bl.tx_hashes, txs, rsp.missed_ids); - CHECK_AND_ASSERT_MES(!missed_tx_id.size(), false, "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() - << ENDL << "for block id = " << get_block_hash(bl)); - rsp.blocks.push_back(block_complete_entry()); - block_complete_entry& e = rsp.blocks.back(); - //pack block - e.block = t_serializable_object_to_blob(bl); - //pack transactions - BOOST_FOREACH(transaction& tx, txs) - e.txs.push_back(t_serializable_object_to_blob(tx)); - + CHECK_AND_ASSERT_MES(!missed_tx_id.size(), false, "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() << ENDL << "for block id = " << get_block_hash(bl)); + rsp.blocks.push_back(block_complete_entry()); + block_complete_entry& e = rsp.blocks.back(); + //pack block + e.block = t_serializable_object_to_blob(bl); + //pack transactions + for (transaction& tx : txs) { + e.txs.push_back(t_serializable_object_to_blob(tx)); + } } + //get another transactions, if need std::list txs; get_transactions(arg.txs, txs, rsp.missed_ids); //pack aside transactions - BOOST_FOREACH(const auto& tx, txs) + for (const auto& tx : txs) { rsp.txs.push_back(t_serializable_object_to_blob(tx)); + } return true; } -//------------------------------------------------------------------ -bool blockchain_storage::get_alternative_blocks(std::list& blocks) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(const auto& alt_bl, m_alternative_chains) - { +bool blockchain_storage::get_alternative_blocks(std::list& blocks) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + for (auto& alt_bl : m_alternative_chains) { blocks.push_back(alt_bl.second.bl); } + return true; } -//------------------------------------------------------------------ -size_t blockchain_storage::get_alternative_blocks_count() -{ + +size_t blockchain_storage::get_alternative_blocks_count() { CRITICAL_REGION_LOCAL(m_blockchain_lock); return m_alternative_chains.size(); } -//------------------------------------------------------------------ -bool blockchain_storage::add_out_to_get_random_outs(std::vector >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) -{ + +bool blockchain_storage::add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - transactions_container::iterator tx_it = m_transactions.find(amount_outs[i].first); - CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "internal error: transaction with id " << amount_outs[i].first << ENDL << - ", used in mounts global index for amount=" << amount << ": i=" << i << "not found in transactions index"); - CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index=" - << amount_outs[i].second << " more than transaction outputs = " << tx_it->second.tx.vout.size() << ", for tx id = " << amount_outs[i].first); - transaction& tx = tx_it->second.tx; + const transaction& tx = transactionByIndex(amount_outs[i].first).tx; + CHECK_AND_ASSERT_MES(tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index=" + << amount_outs[i].second << " more than transaction outputs = " << tx.vout.size() << ", for tx id = " << get_transaction_hash(tx)); CHECK_AND_ASSERT_MES(tx.vout[amount_outs[i].second].target.type() == typeid(txout_to_key), false, "unknown tx out type"); //check if transaction is unlocked - if(!is_tx_spendtime_unlocked(tx.unlock_time)) + if (!is_tx_spendtime_unlocked(tx.unlock_time)) 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()); @@ -942,78 +1021,74 @@ bool blockchain_storage::add_out_to_get_random_outs(std::vector(tx.vout[amount_outs[i].second].target).key; return true; } -//------------------------------------------------------------------ -size_t blockchain_storage::find_end_of_allowed_index(const std::vector >& amount_outs) -{ + +size_t blockchain_storage::find_end_of_allowed_index(const std::vector>& amount_outs) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!amount_outs.size()) + if (amount_outs.empty()) { return 0; + } + size_t i = amount_outs.size(); - do - { + do { --i; - transactions_container::iterator it = m_transactions.find(amount_outs[i].first); - CHECK_AND_ASSERT_MES(it != m_transactions.end(), 0, "internal error: failed to find transaction from outputs index with tx_id=" << amount_outs[i].first); - if(it->second.m_keeper_block_height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW <= get_current_blockchain_height() ) - return i+1; + if (amount_outs[i].first.block + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW <= get_current_blockchain_height()) { + return i + 1; + } } while (i != 0); + return 0; } -//------------------------------------------------------------------ -bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) -{ + +bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) { srand(static_cast(time(NULL))); CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(uint64_t amount, req.amounts) - { + for (uint64_t amount : req.amounts) { 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); - if(it == m_outputs.end()) - { + if (it == m_outputs.end()) { LOG_ERROR("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"); 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 } - std::vector >& amount_outs = it->second; + + std::vector>& amount_outs = it->second; //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); CHECK_AND_ASSERT_MES(up_index_limit <= amount_outs.size(), false, "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size()); - if(amount_outs.size() > req.outs_count) - { + if (amount_outs.size() > req.outs_count) { std::set used; size_t try_count = 0; - for(uint64_t j = 0; j != req.outs_count && try_count < up_index_limit;) - { - size_t i = rand()%up_index_limit; - if(used.count(i)) + for (uint64_t j = 0; j != req.outs_count && try_count < up_index_limit;) { + size_t i = rand() % up_index_limit; + if (used.count(i)) continue; bool added = add_out_to_get_random_outs(amount_outs, result_outs, amount, i); used.insert(i); - if(added) + if (added) ++j; ++try_count; } - }else - { - for(size_t i = 0; i != up_index_limit; i++) + } else { + for (size_t i = 0; i != up_index_limit; i++) { add_out_to_get_random_outs(amount_outs, result_outs, amount, i); + } } } return true; } -//------------------------------------------------------------------ + bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!qblock_ids.size() /*|| !req.m_total_height*/) + if (!qblock_ids.size() /*|| !req.m_total_height*/) { LOG_ERROR("Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection"); return false; } //check genesis match - if(qblock_ids.back() != get_block_hash(m_blocks[0].bl)) + if (qblock_ids.back() != get_block_hash(m_blocks[0].bl)) { LOG_ERROR("Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << ENDL << "id: " << qblock_ids.back() << ", " << ENDL << "expected: " << get_block_hash(m_blocks[0].bl) @@ -1024,21 +1099,21 @@ bool blockchain_storage::find_blockchain_supplement(const std::listsecond; return true; } -//------------------------------------------------------------------ + uint64_t blockchain_storage::block_difficulty(size_t i) { CRITICAL_REGION_LOCAL(m_blockchain_lock); CHECK_AND_ASSERT_MES(i < m_blocks.size(), false, "wrong block index i = " << i << " at blockchain_storage::block_difficulty()"); - if(i == 0) + if (i == 0) return m_blocks[i].cumulative_difficulty; - return m_blocks[i].cumulative_difficulty - m_blocks[i-1].cumulative_difficulty; + return m_blocks[i].cumulative_difficulty - m_blocks[i - 1].cumulative_difficulty; } -//------------------------------------------------------------------ + void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) { std::stringstream ss; CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(start_index >=m_blocks.size()) + if (start_index >= m_blocks.size()) { - LOG_PRINT_L0("Wrong starter index set: " << start_index << ", expected max index " << m_blocks.size()-1); + LOG_PRINT_L0("Wrong starter index set: " << start_index << ", expected max index " << m_blocks.size() - 1); return; } - for(size_t i = start_index; i != m_blocks.size() && i != end_index; i++) + for (size_t i = start_index; i != m_blocks.size() && i != end_index; i++) { ss << "height " << i << ", timestamp " << m_blocks[i].bl.timestamp << ", cumul_dif " << m_blocks[i].cumulative_difficulty << ", cumul_size " << m_blocks[i].block_cumulative_size - << "\nid\t\t" << get_block_hash(m_blocks[i].bl) + << "\nid\t\t" << get_block_hash(m_blocks[i].bl) << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.tx_hashes.size() << ENDL; } LOG_PRINT_L1("Current blockchain:" << ENDL << ss.str()); LOG_PRINT_L0("Blockchain printed with log level 1"); } -//------------------------------------------------------------------ -void blockchain_storage::print_blockchain_index() -{ + +void blockchain_storage::print_blockchain_index() { std::stringstream ss; CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(const blocks_by_id_index::value_type& v, m_blocks_index) - ss << "id\t\t" << v.first << " height" << v.second << ENDL << ""; + for (auto& i : m_blockMap) { + ss << "id\t\t" << i.first << " height" << i.second << ENDL << ""; + } LOG_PRINT_L0("Current blockchain index:" << ENDL << ss.str()); } -//------------------------------------------------------------------ -void blockchain_storage::print_blockchain_outs(const std::string& file) -{ + +void blockchain_storage::print_blockchain_outs(const std::string& file) { std::stringstream ss; CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(const outputs_container::value_type& v, m_outputs) - { - const std::vector >& vals = v.second; - if(vals.size()) - { - ss << "amount: " << v.first << ENDL; - for(size_t i = 0; i != vals.size(); i++) - ss << "\t" << vals[i].first << ": " << vals[i].second << ENDL; + for (const outputs_container::value_type& v : m_outputs) { + const std::vector>& vals = v.second; + if (!vals.empty()) { + ss << "amount: " << v.first << ENDL; + for (size_t i = 0; i != vals.size(); i++) { + ss << "\t" << get_transaction_hash(transactionByIndex(vals[i].first).tx) << ": " << vals[i].second << ENDL; + } } } - if(file_io_utils::save_string_to_file(file, ss.str())) - { + + if (epee::file_io_utils::save_string_to_file(file, ss.str())) { LOG_PRINT_L0("Current outputs index writen to file: " << file); - }else - { + } else { LOG_PRINT_L0("Failed to write current outputs index to file: " << file); } } -//------------------------------------------------------------------ -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) -{ + +bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!find_blockchain_supplement(qblock_ids, resp.start_height)) + if (!find_blockchain_supplement(qblock_ids, resp.start_height)) return false; resp.total_height = get_current_blockchain_height(); size_t count = 0; - for(size_t i = resp.start_height; i != m_blocks.size() && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) - resp.m_block_ids.push_back(get_block_hash(m_blocks[i].bl)); + for (size_t i = resp.start_height; i != m_blocks.size() && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) { + crypto::hash h; + if (!get_block_hash(m_blocks[i].bl, h)) { + return false; + } + + resp.m_block_ids.push_back(h); + } + return true; } -//------------------------------------------------------------------ -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) -{ + +bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!find_blockchain_supplement(qblock_ids, start_height)) + if (!find_blockchain_supplement(qblock_ids, start_height)) { return false; + } total_height = get_current_blockchain_height(); size_t count = 0; - for(size_t i = start_height; i != m_blocks.size() && count < max_count; i++, count++) - { - blocks.resize(blocks.size()+1); + for (size_t i = start_height; i != m_blocks.size() && count < max_count; i++, count++) { + blocks.resize(blocks.size() + 1); blocks.back().first = m_blocks[i].bl; std::list mis; get_transactions(m_blocks[i].bl.tx_hashes, blocks.back().second, mis); CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); } + return true; } -//------------------------------------------------------------------ -bool blockchain_storage::add_block_as_invalid(const block& bl, const crypto::hash& h) -{ - block_extended_info bei = AUTO_VAL_INIT(bei); - bei.bl = bl; - return add_block_as_invalid(bei, h); -} -//------------------------------------------------------------------ -bool blockchain_storage::add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto i_res = m_invalid_blocks.insert(std::map::value_type(h, bei)); - CHECK_AND_ASSERT_MES(i_res.second, false, "at insertion invalid by tx returned status existed"); - LOG_PRINT_L0("BLOCK ADDED AS INVALID: " << h << ENDL << ", prev_id=" << bei.bl.prev_id << ", m_invalid_blocks count=" << m_invalid_blocks.size()); - return true; -} -//------------------------------------------------------------------ + bool blockchain_storage::have_block(const crypto::hash& id) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(m_blocks_index.count(id)) + if (m_blockMap.count(id)) return true; - if(m_alternative_chains.count(id)) - return true; - /*if(m_orphaned_blocks.get().count(id)) - return true;*/ - /*if(m_orphaned_by_tx.count(id)) - return true;*/ - if(m_invalid_blocks.count(id)) + if (m_alternative_chains.count(id)) return true; return false; } -//------------------------------------------------------------------ -bool blockchain_storage::handle_block_to_main_chain(const block& bl, block_verification_context& bvc) -{ - crypto::hash id = get_block_hash(bl); - return handle_block_to_main_chain(bl, id, bvc); -} -//------------------------------------------------------------------ -bool blockchain_storage::push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector& global_indexes) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - size_t i = 0; - BOOST_FOREACH(const auto& ot, tx.vout) - { - outputs_container::mapped_type& amount_index = m_outputs[ot.amount]; - amount_index.push_back(std::pair(tx_id, i)); - global_indexes.push_back(amount_index.size()-1); - ++i; - } - return true; -} -//------------------------------------------------------------------ -size_t blockchain_storage::get_total_transactions() -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - return m_transactions.size(); -} -//------------------------------------------------------------------ -bool blockchain_storage::get_outs(uint64_t amount, std::list& pkeys) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto it = m_outputs.find(amount); - if(it == m_outputs.end()) - return true; - BOOST_FOREACH(const auto& out_entry, it->second) - { - auto tx_it = m_transactions.find(out_entry.first); - CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "transactions outs global index consistency broken: wrong tx id in index"); - CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > out_entry.second, false, "transactions outs global index consistency broken: index in tx_outx more then size"); - CHECK_AND_ASSERT_MES(tx_it->second.tx.vout[out_entry.second].target.type() == typeid(txout_to_key), false, "transactions outs global index consistency broken: index in tx_outx more then size"); - pkeys.push_back(boost::get(tx_it->second.tx.vout[out_entry.second].target).key); - } - - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id) -{ +size_t blockchain_storage::get_total_transactions() { CRITICAL_REGION_LOCAL(m_blockchain_lock); - size_t i = tx.vout.size()-1; - BOOST_REVERSE_FOREACH(const auto& ot, tx.vout) - { - auto it = m_outputs.find(ot.amount); - CHECK_AND_ASSERT_MES(it != m_outputs.end(), false, "transactions outs global index consistency broken"); - CHECK_AND_ASSERT_MES(it->second.size(), false, "transactions outs global index: empty index for amount: " << ot.amount); - CHECK_AND_ASSERT_MES(it->second.back().first == tx_id , false, "transactions outs global index consistency broken: tx id missmatch"); - CHECK_AND_ASSERT_MES(it->second.back().second == i, false, "transactions outs global index consistency broken: in transaction index missmatch"); - it->second.pop_back(); - --i; - } - return true; + return m_transactionMap.size(); } -//------------------------------------------------------------------ -bool blockchain_storage::add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - struct add_transaction_input_visitor: public boost::static_visitor - { - key_images_container& m_spent_keys; - const crypto::hash& m_tx_id; - const crypto::hash& m_bl_id; - add_transaction_input_visitor(key_images_container& spent_keys, const crypto::hash& tx_id, const crypto::hash& bl_id):m_spent_keys(spent_keys), m_tx_id(tx_id), m_bl_id(bl_id) - {} - bool operator()(const txin_to_key& in) const - { - const crypto::key_image& ki = in.k_image; - auto r = m_spent_keys.insert(ki); - if(!r.second) - { - //double spend detected - LOG_PRINT_L0("tx with id: " << m_tx_id << " in block id: " << m_bl_id << " have input marked as spent with key image: " << ki << ", block declined"); - return false; - } - return true; - } - bool operator()(const txin_gen& tx) const{return true;} - bool operator()(const txin_to_script& tx) const{return false;} - bool operator()(const txin_to_scripthash& tx) const{return false;} - }; - - BOOST_FOREACH(const txin_v& in, tx.vin) - { - if(!boost::apply_visitor(add_transaction_input_visitor(m_spent_keys, tx_id, bl_id), in)) - { - LOG_ERROR("critical internal error: add_transaction_input_visitor failed. but here key_images should be shecked"); - purge_transaction_keyimages_from_blockchain(tx, false); - return false; - } - } - transaction_chain_entry ch_e; - ch_e.m_keeper_block_height = bl_height; - ch_e.tx = tx; - auto i_r = m_transactions.insert(std::pair(tx_id, ch_e)); - if(!i_r.second) - { - LOG_PRINT_L0("tx with id: " << tx_id << " in block id: " << bl_id << " already in blockchain"); - return false; - } - bool r = push_transaction_to_global_outs_index(tx, tx_id, i_r.first->second.m_global_output_indexes); - CHECK_AND_ASSERT_MES(r, false, "failed to return push_transaction_to_global_outs_index tx id " << tx_id); - LOG_PRINT_L2("Added transaction to blockchain history:" << ENDL - << "tx_id: " << tx_id << ENDL - << "inputs: " << tx.vin.size() << ", outs: " << tx.vout.size() << ", spend money: " << print_money(get_outs_money_amount(tx)) << "(fee: " << (is_coinbase(tx) ? "0[coinbase]" : print_money(get_tx_fee(tx))) << ")"); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) -{ +bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto it = m_transactions.find(tx_id); - if(it == m_transactions.end()) - { + auto it = m_transactionMap.find(tx_id); + if (it == m_transactionMap.end()) { LOG_PRINT_RED_L0("warning: get_tx_outputs_gindexs failed to find transaction with id = " << tx_id); return false; } - CHECK_AND_ASSERT_MES(it->second.m_global_output_indexes.size(), false, "internal error: global indexes for transaction " << tx_id << " is empty"); - indexs = it->second.m_global_output_indexes; + const Transaction& tx = transactionByIndex(it->second); + CHECK_AND_ASSERT_MES(tx.m_global_output_indexes.size(), false, "internal error: global indexes for transaction " << tx_id << " is empty"); + 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]; + } + return true; } -//------------------------------------------------------------------ -bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) -{ + +bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) { CRITICAL_REGION_LOCAL(m_blockchain_lock); bool res = check_tx_inputs(tx, &max_used_block_height); - if(!res) return false; - CHECK_AND_ASSERT_MES(max_used_block_height < m_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size()); + if (!res) return false; + CHECK_AND_ASSERT_MES(max_used_block_height < m_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size()); get_block_hash(m_blocks[max_used_block_height].bl, max_used_block_id); return true; } -//------------------------------------------------------------------ -bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) -{ - BOOST_FOREACH(const txin_v& in, tx.vin) - { + +bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) { + for(const txin_v& in : tx.vin) { CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, in_to_key, true); - if(have_tx_keyimg_as_spent(in_to_key.k_image)) + if (have_tx_keyimg_as_spent(in_to_key.k_image)) { return true; + } } + return false; } -//------------------------------------------------------------------ -bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) -{ + +bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) { crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height); } -//------------------------------------------------------------------ -bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) -{ - size_t sig_index = 0; - if(pmax_used_block_height) - *pmax_used_block_height = 0; - BOOST_FOREACH(const auto& txin, tx.vin) - { +bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) { + size_t sig_index = 0; + if (pmax_used_block_height) { + *pmax_used_block_height = 0; + } + + for (const auto& txin : tx.vin) { CHECK_AND_ASSERT_MES(txin.type() == typeid(txin_to_key), false, "wrong type id in tx input at blockchain_storage::check_tx_inputs"); const txin_to_key& in_to_key = boost::get(txin); CHECK_AND_ASSERT_MES(in_to_key.key_offsets.size(), false, "empty in_to_key.key_offsets in transaction with id " << get_transaction_hash(tx)); - if(have_tx_keyimg_as_spent(in_to_key.k_image)) + if (have_tx_keyimg_as_spent(in_to_key.k_image)) { - LOG_PRINT_L1("Key image already spent in blockchain: " << string_tools::pod_to_hex(in_to_key.k_image)); + LOG_PRINT_L1("Key image already spent in blockchain: " << epee::string_tools::pod_to_hex(in_to_key.k_image)); return false; } CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index); - if(!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pmax_used_block_height)) - { + if (!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pmax_used_block_height)) { LOG_PRINT_L0("Failed to check ring signature for tx " << get_transaction_hash(tx)); return false; } @@ -1368,48 +1312,43 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha return true; } -//------------------------------------------------------------------ -bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) -{ - if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) - { + +bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) { + if (unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) { //interpret as block index - if(get_current_blockchain_height()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) + if (get_current_blockchain_height() - 1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) return true; else return false; - }else - { + } else { //interpret as time uint64_t current_time = static_cast(time(NULL)); - if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlock_time) + if (current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlock_time) return true; else return false; } + return false; } -//------------------------------------------------------------------ -bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height) -{ + +bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); struct outputs_visitor { std::vector& m_results_collector; blockchain_storage& m_bch; - outputs_visitor(std::vector& results_collector, blockchain_storage& bch):m_results_collector(results_collector), m_bch(bch) + outputs_visitor(std::vector& results_collector, blockchain_storage& bch) :m_results_collector(results_collector), m_bch(bch) {} - bool handle_output(const transaction& tx, const tx_out& out) - { + bool handle_output(const transaction& tx, const tx_out& out) { //check tx unlock time - if(!m_bch.is_tx_spendtime_unlocked(tx.unlock_time)) - { + if (!m_bch.is_tx_spendtime_unlocked(tx.unlock_time)) { LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlock_time = " << tx.unlock_time); return false; } - if(out.target.type() != typeid(txout_to_key)) + if (out.target.type() != typeid(txout_to_key)) { LOG_PRINT_L0("Output have wrong type id, which=" << out.target.which()); return false; @@ -1423,255 +1362,340 @@ bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::h //check ring signature std::vector output_keys; outputs_visitor vi(output_keys, *this); - if(!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height)) - { + if (!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height)) { LOG_PRINT_L0("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size()); return false; } - if(txin.key_offsets.size() != output_keys.size()) - { + if (txin.key_offsets.size() != output_keys.size()) { LOG_PRINT_L0("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size()); return false; } + CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); - if(m_is_in_checkpoint_zone) + if (m_is_in_checkpoint_zone) { return true; + } + return crypto::check_ring_signature(tx_prefix_hash, txin.k_image, output_keys, sig.data()); } -//------------------------------------------------------------------ -uint64_t blockchain_storage::get_adjusted_time() -{ + +uint64_t blockchain_storage::get_adjusted_time() { //TODO: add collecting median time return time(NULL); } -//------------------------------------------------------------------ -bool blockchain_storage::check_block_timestamp_main(const block& b) -{ - if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT) - { + +bool blockchain_storage::check_block_timestamp_main(const block& b) { + if (b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT) { LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours"); return false; } std::vector timestamps; - size_t offset = m_blocks.size() <= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ? 0: m_blocks.size()- BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; - for(;offset!= m_blocks.size(); ++offset) + size_t offset = m_blocks.size() <= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ? 0 : m_blocks.size() - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + for (; offset != m_blocks.size(); ++offset) { timestamps.push_back(m_blocks[offset].bl.timestamp); + } return check_block_timestamp(std::move(timestamps), b); } -//------------------------------------------------------------------ -bool blockchain_storage::check_block_timestamp(std::vector timestamps, const block& b) -{ - if(timestamps.size() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) + +bool blockchain_storage::check_block_timestamp(std::vector timestamps, const block& b) { + if (timestamps.size() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) { return true; + } uint64_t median_ts = epee::misc_utils::median(timestamps); - if(b.timestamp < median_ts) - { + if (b.timestamp < median_ts) { LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts); return false; } return true; } -//------------------------------------------------------------------ -bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc) -{ - TIME_MEASURE_START(block_processing_time); - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(bl.prev_id != get_tail_id()) - { - LOG_PRINT_L0("Block with id: " << id << ENDL - << "have wrong prev_id: " << bl.prev_id << ENDL - << "expected: " << get_tail_id()); - return false; - } - if(!check_block_timestamp_main(bl)) - { - LOG_PRINT_L0("Block with id: " << id << ENDL - << "have invalid timestamp: " << bl.timestamp); - //add_block_as_invalid(bl, id);//do not add blocks to invalid storage befor proof of work check was passed - bvc.m_verifivation_failed = true; - return false; - } - - //check proof of work - TIME_MEASURE_START(target_calculating_time); - difficulty_type current_diffic = get_difficulty_for_next_block(); - CHECK_AND_ASSERT_MES(current_diffic, false, "!!!!!!!!! difficulty overhead !!!!!!!!!"); - TIME_MEASURE_FINISH(target_calculating_time); - TIME_MEASURE_START(longhash_calculating_time); - crypto::hash proof_of_work = null_hash; - if(!m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())) - { - proof_of_work = get_block_longhash(bl, m_blocks.size()); - - if(!check_hash(proof_of_work, current_diffic)) - { - LOG_PRINT_L0("Block with id: " << id << ENDL - << "have not enough proof of work: " << proof_of_work << ENDL - << "nexpected difficulty: " << current_diffic ); - bvc.m_verifivation_failed = true; - return false; - } - }else - { - if(!m_checkpoints.check_block(get_current_blockchain_height(), id)) - { - LOG_ERROR("CHECKPOINT VALIDATION FAILED"); - bvc.m_verifivation_failed = true; - return false; - } - } - TIME_MEASURE_FINISH(longhash_calculating_time); - - if(!prevalidate_miner_transaction(bl, m_blocks.size())) - { - LOG_PRINT_L0("Block with id: " << id - << " failed to pass prevalidation"); - bvc.m_verifivation_failed = true; - return false; - } - size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx); - size_t cumulative_block_size = coinbase_blob_size; - //process transactions - if(!add_transaction_from_block(bl.miner_tx, get_transaction_hash(bl.miner_tx), id, get_current_blockchain_height())) - { - LOG_PRINT_L0("Block with id: " << id << " failed to add transaction to blockchain storage"); - bvc.m_verifivation_failed = true; - return false; - } - size_t tx_processed_count = 0; - uint64_t fee_summary = 0; - BOOST_FOREACH(const crypto::hash& tx_id, bl.tx_hashes) - { - transaction tx; - size_t blob_size = 0; - uint64_t fee = 0; - if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee)) - { - LOG_PRINT_L0("Block with id: " << id << "have at least one unknown transaction with id: " << tx_id); - purge_block_data_from_blockchain(bl, tx_processed_count); - //add_block_as_invalid(bl, id); - bvc.m_verifivation_failed = true; - return false; - } - if(!check_tx_inputs(tx)) - { - LOG_PRINT_L0("Block with id: " << id << "have at least one transaction (id: " << tx_id << ") with wrong inputs."); - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - bool add_res = m_tx_pool.add_tx(tx, tvc, true); - CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool"); - purge_block_data_from_blockchain(bl, tx_processed_count); - add_block_as_invalid(bl, id); - LOG_PRINT_L0("Block with id " << id << " added as invalid becouse of wrong inputs in transactions"); - bvc.m_verifivation_failed = true; - return false; - } - - if(!add_transaction_from_block(tx, tx_id, id, get_current_blockchain_height())) - { - LOG_PRINT_L0("Block with id: " << id << " failed to add transaction to blockchain storage"); - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - bool add_res = m_tx_pool.add_tx(tx, tvc, true); - CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool"); - purge_block_data_from_blockchain(bl, tx_processed_count); - bvc.m_verifivation_failed = true; - return false; - } - fee_summary += fee; - cumulative_block_size += blob_size; - ++tx_processed_count; - } - uint64_t base_reward = 0; - uint64_t already_generated_coins = m_blocks.size() ? m_blocks.back().already_generated_coins:0; - if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins)) - { - LOG_PRINT_L0("Block with id: " << id - << " have wrong miner transaction"); - purge_block_data_from_blockchain(bl, tx_processed_count); - bvc.m_verifivation_failed = true; - return false; - } - - - block_extended_info bei = boost::value_initialized(); - bei.bl = bl; - bei.block_cumulative_size = cumulative_block_size; - bei.cumulative_difficulty = current_diffic; - bei.already_generated_coins = already_generated_coins + base_reward; - if(m_blocks.size()) - bei.cumulative_difficulty += m_blocks.back().cumulative_difficulty; - - bei.height = m_blocks.size(); - - auto ind_res = m_blocks_index.insert(std::pair(id, bei.height)); - if(!ind_res.second) - { - LOG_ERROR("block with id: " << id << " already in block indexes"); - purge_block_data_from_blockchain(bl, tx_processed_count); - bvc.m_verifivation_failed = true; - return false; - } - - m_blocks.push_back(bei); - update_next_comulative_size_limit(); - TIME_MEASURE_FINISH(block_processing_time); - LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << id - << ENDL << "PoW:\t" << proof_of_work - << ENDL << "HEIGHT " << bei.height << ", difficulty:\t" << current_diffic - << ENDL << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) - << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size - << ", " << block_processing_time << "("<< target_calculating_time << "/" << longhash_calculating_time << ")ms"); - - bvc.m_added_to_main_chain = true; - /*if(!m_orphanes_reorganize_in_work) - review_orphaned_blocks_with_new_block_id(id, true);*/ - - m_tx_pool.on_blockchain_inc(bei.height, id); - //LOG_PRINT_L0("BLOCK: " << ENDL << "" << dump_obj_as_json(bei.bl)); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::update_next_comulative_size_limit() -{ +bool blockchain_storage::update_next_comulative_size_limit() { std::vector sz; get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - uint64_t median = misc_utils::median(sz); - if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) + uint64_t median = epee::misc_utils::median(sz); + if (median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; - m_current_block_cumul_sz_limit = median*2; + m_current_block_cumul_sz_limit = median * 2; return true; } -//------------------------------------------------------------------ -bool blockchain_storage::add_new_block(const block& bl_, block_verification_context& bvc) -{ + +bool blockchain_storage::add_new_block(const block& bl_, block_verification_context& bvc) { //copy block here to let modify block.target block bl = bl_; crypto::hash id = get_block_hash(bl); CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process CRITICAL_REGION_LOCAL1(m_blockchain_lock); - if(have_block(id)) - { + if (have_block(id)) { LOG_PRINT_L3("block with id = " << id << " already exists"); bvc.m_already_exists = true; return false; } //check that block refers to chain tail - if(!(bl.prev_id == get_tail_id())) - { + if (!(bl.prev_id == get_tail_id())) { //chain switching or wrong block bvc.m_added_to_main_chain = false; return handle_alternative_block(bl, id, bvc); //never relay alternative blocks } - return handle_block_to_main_chain(bl, id, bvc); -} \ No newline at end of file + return pushBlock(bl, bvc); +} + +const blockchain_storage::Transaction& blockchain_storage::transactionByIndex(TransactionIndex index) { + return m_blocks[index.block].transactions[index.transaction]; +} + +bool blockchain_storage::pushBlock(const block& blockData, block_verification_context& bvc) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + TIME_MEASURE_START(block_processing_time); + + crypto::hash blockHash = get_block_hash(blockData); + + if (m_blockMap.count(blockHash) != 0) { + LOG_ERROR("Block " << blockHash << " already exists in blockchain."); + bvc.m_verifivation_failed = true; + return false; + } + + if (blockData.prev_id != get_tail_id()) { + LOG_PRINT_L0("Block " << blockHash << " has wrong prev_id: " << blockData.prev_id << ", expected: " << get_tail_id()); + bvc.m_verifivation_failed = true; + return false; + } + + if (!check_block_timestamp_main(blockData)) { + LOG_PRINT_L0("Block " << blockHash << " has invalid timestamp: " << blockData.timestamp); + bvc.m_verifivation_failed = true; + return false; + } + + TIME_MEASURE_START(target_calculating_time); + difficulty_type currentDifficulty = get_difficulty_for_next_block(); + TIME_MEASURE_FINISH(target_calculating_time); + CHECK_AND_ASSERT_MES(currentDifficulty, false, "!!!!!!!!! difficulty overhead !!!!!!!!!"); + + TIME_MEASURE_START(longhash_calculating_time); + crypto::hash proof_of_work = null_hash; + if (m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())) { + if (!m_checkpoints.check_block(get_current_blockchain_height(), blockHash)) { + LOG_ERROR("CHECKPOINT VALIDATION FAILED"); + bvc.m_verifivation_failed = true; + return false; + } + } else { + proof_of_work = get_block_longhash(blockData, m_blocks.size()); + if (!check_hash(proof_of_work, currentDifficulty)) { + LOG_PRINT_L0("Block " << blockHash << ", has too weak proof of work: " << proof_of_work << ", expected difficulty: " << currentDifficulty); + bvc.m_verifivation_failed = true; + return false; + } + } + + TIME_MEASURE_FINISH(longhash_calculating_time); + + if (!prevalidate_miner_transaction(blockData, m_blocks.size())) { + LOG_PRINT_L0("Block " << blockHash << " failed to pass prevalidation"); + bvc.m_verifivation_failed = true; + return false; + } + + crypto::hash minerTransactionHash = get_transaction_hash(blockData.miner_tx); + + Block block; + block.bl = blockData; + block.transactions.resize(1); + block.transactions[0].tx = blockData.miner_tx; + TransactionIndex transactionIndex = { static_cast(m_blocks.size()), static_cast(0) }; + pushTransaction(block, minerTransactionHash, transactionIndex); + + size_t coinbase_blob_size = get_object_blobsize(blockData.miner_tx); + size_t cumulative_block_size = coinbase_blob_size; + uint64_t fee_summary = 0; + for (const crypto::hash& tx_id : blockData.tx_hashes) { + block.transactions.resize(block.transactions.size() + 1); + size_t blob_size = 0; + uint64_t fee = 0; + if (!m_tx_pool.take_tx(tx_id, block.transactions.back().tx, blob_size, fee)) { + LOG_PRINT_L0("Block " << blockHash << " has at least one unknown transaction: " << tx_id); + bvc.m_verifivation_failed = true; + tx_verification_context tvc = ::AUTO_VAL_INIT(tvc); + block.transactions.pop_back(); + popTransactions(block, minerTransactionHash); + return false; + } + + if (!check_tx_inputs(block.transactions.back().tx)) { + LOG_PRINT_L0("Block " << blockHash << " has at least one transaction with wrong inputs: " << tx_id); + bvc.m_verifivation_failed = true; + tx_verification_context tvc = ::AUTO_VAL_INIT(tvc); + if (!m_tx_pool.add_tx(block.transactions.back().tx, tvc, true)) { + LOG_ERROR("Cannot move transaction from blockchain to transaction pool."); + } + + block.transactions.pop_back(); + popTransactions(block, minerTransactionHash); + return false; + } + + ++transactionIndex.transaction; + pushTransaction(block, tx_id, transactionIndex); + + cumulative_block_size += blob_size; + fee_summary += fee; + } + + uint64_t base_reward = 0; + uint64_t already_generated_coins = m_blocks.size() ? m_blocks.back().already_generated_coins : 0; + if (!validate_miner_transaction(blockData, cumulative_block_size, fee_summary, base_reward, already_generated_coins)) { + LOG_PRINT_L0("Block " << blockHash << " has invalid miner transaction"); + bvc.m_verifivation_failed = true; + popTransactions(block, minerTransactionHash); + return false; + } + + block.height = static_cast(m_blocks.size()); + block.block_cumulative_size = cumulative_block_size; + block.cumulative_difficulty = currentDifficulty; + block.already_generated_coins = already_generated_coins + base_reward; + if (m_blocks.size() > 0) { + block.cumulative_difficulty += m_blocks.back().cumulative_difficulty; + } + + pushBlock(block); + update_next_comulative_size_limit(); + TIME_MEASURE_FINISH(block_processing_time); + LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << blockHash + << ENDL << "PoW:\t" << proof_of_work + << ENDL << "HEIGHT " << block.height << ", difficulty:\t" << currentDifficulty + << ENDL << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) + << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size + << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms"); + + bvc.m_added_to_main_chain = true; + return true; +} + +bool blockchain_storage::pushBlock(Block& block) { + crypto::hash blockHash = get_block_hash(block.bl); + auto result = m_blockMap.insert(std::make_pair(blockHash, static_cast(m_blocks.size()))); + if (!result.second) { + LOG_ERROR("Duplicate block was pushed to blockchain."); + return false; + } + + m_blocks.push_back(block); + return true; +} + +void blockchain_storage::popBlock(const crypto::hash& blockHash) { + if (m_blocks.empty()) { + LOG_ERROR("Attempt to pop block from empty blockchain."); + return; + } + + popTransactions(m_blocks.back(), get_transaction_hash(m_blocks.back().bl.miner_tx)); + m_blocks.pop_back(); + size_t count = m_blockMap.erase(blockHash); + if (count != 1) { + LOG_ERROR("Blockchain consistency broken - cannot find block by hash."); + } +} + +bool blockchain_storage::pushTransaction(Block& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex) { + auto result = m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); + if (!result.second) { + LOG_ERROR("Duplicate transaction was pushed to blockchain."); + return false; + } + + Transaction& transaction = block.transactions[transactionIndex.transaction]; + for (size_t i = 0; i < transaction.tx.vin.size(); ++i) { + if (transaction.tx.vin[i].type() == typeid(txin_to_key)) { + auto result = m_spent_keys.insert(::boost::get(transaction.tx.vin[i]).k_image); + if (!result.second) { + LOG_ERROR("Double spending transaction was pushed to blockchain."); + for (size_t j = 0; j < i; ++j) { + m_spent_keys.erase(::boost::get(transaction.tx.vin[i - 1 - j]).k_image); + } + + m_transactionMap.erase(transactionHash); + return false; + } + } + } + + transaction.m_global_output_indexes.resize(transaction.tx.vout.size()); + for (uint16_t output = 0; output < transaction.tx.vout.size(); ++output) { + auto& amountOutputs = m_outputs[transaction.tx.vout[output].amount]; + transaction.m_global_output_indexes[output] = amountOutputs.size(); + amountOutputs.push_back(std::make_pair<>(transactionIndex, output)); + } + + return true; +} + +void blockchain_storage::popTransaction(const transaction& transaction, const crypto::hash& transactionHash) { + TransactionIndex transactionIndex = m_transactionMap.at(transactionHash); + for (size_t output = 0; output < transaction.vout.size(); ++output) { + auto amountOutputs = m_outputs.find(transaction.vout[transaction.vout.size() - 1 - output].amount); + if (amountOutputs == m_outputs.end()) { + LOG_ERROR("Blockchain consistency broken - cannot find specific amount in outputs map."); + continue; + } + + if (amountOutputs->second.empty()) { + LOG_ERROR("Blockchain consistency broken - output array for specific amount is empty."); + continue; + } + + if (amountOutputs->second.back().first.block != transactionIndex.block || amountOutputs->second.back().first.transaction != transactionIndex.transaction) { + LOG_ERROR("Blockchain consistency broken - invalid transaction index."); + continue; + } + + if (amountOutputs->second.back().second != transaction.vout.size() - 1 - output) { + LOG_ERROR("Blockchain consistency broken - invalid output index."); + continue; + } + + amountOutputs->second.pop_back(); + if (amountOutputs->second.empty()) { + m_outputs.erase(amountOutputs); + } + } + + for (auto& input : transaction.vin) { + if (input.type() == typeid(txin_to_key)) { + size_t count = m_spent_keys.erase(::boost::get(input).k_image); + if (count != 1) { + LOG_ERROR("Blockchain consistency broken - cannot find spent key."); + } + } + } + + size_t count = m_transactionMap.erase(transactionHash); + if (count != 1) { + LOG_ERROR("Blockchain consistency broken - cannot find transaction by hash."); + } +} + +void blockchain_storage::popTransactions(const Block& block, const crypto::hash& minerTransactionHash) { + for (size_t i = 0; i < block.transactions.size() - 1; ++i) { + popTransaction(block.transactions[block.transactions.size() - 1 - i].tx, block.bl.tx_hashes[block.transactions.size() - 2 - i]); + tx_verification_context tvc = ::AUTO_VAL_INIT(tvc); + if (!m_tx_pool.add_tx(block.transactions[block.transactions.size() - 1 - i].tx, tvc, true)) { + LOG_ERROR("Cannot move transaction from blockchain to transaction pool."); + } + } + + popTransaction(block.bl.miner_tx, minerTransactionHash); +} diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index b1fb5df4..1cbb3dab 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -3,55 +3,17 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "syncobj.h" -#include "string_tools.h" +#include "SwappedVector.h" +#include "cryptonote_format_utils.h" #include "tx_pool.h" -#include "cryptonote_basic.h" #include "common/util.h" -#include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "rpc/core_rpc_server_commands_defs.h" -#include "difficulty.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "verification_context.h" -#include "crypto/hash.h" #include "checkpoints.h" -namespace cryptonote -{ - - /************************************************************************/ - /* */ - /************************************************************************/ - class blockchain_storage - { +namespace cryptonote { + class blockchain_storage { public: - struct transaction_chain_entry - { - transaction tx; - uint64_t m_keeper_block_height; - size_t m_blob_size; - std::vector m_global_output_indexes; - }; - - struct block_extended_info - { - block bl; - uint64_t height; - size_t block_cumulative_size; - difficulty_type cumulative_difficulty; - uint64_t already_generated_coins; - }; - blockchain_storage(tx_memory_pool& tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false) {}; @@ -60,26 +22,17 @@ namespace cryptonote bool deinit(); void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; } - - //bool push_new_block(); bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); bool get_alternative_blocks(std::list& blocks); size_t get_alternative_blocks_count(); crypto::hash get_block_id_by_height(uint64_t height); bool get_block_by_hash(const crypto::hash &h, block &blk); - void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); - template - void serialize(archive_t & ar, const unsigned int version); + template void serialize(archive_t & ar, const unsigned int version); bool have_tx(const crypto::hash &id); bool have_tx_keyimges_as_spent(const transaction &tx); - bool have_tx_keyimg_as_spent(const crypto::key_image &key_im); - transaction *get_tx(const crypto::hash &id); - - template - bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); uint64_t get_current_blockchain_height(); crypto::hash get_tail_id(); @@ -90,231 +43,190 @@ namespace cryptonote bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce); bool have_block(const crypto::hash& id); size_t get_total_transactions(); - bool get_outs(uint64_t amount, std::list& pkeys); bool get_short_chain_history(std::list& ids); bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); - bool find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset); - bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); + bool find_blockchain_supplement(const std::list& qblock_ids, std::list>>& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp); - bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); bool get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count); bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); bool store_blockchain(); - bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height = NULL); - bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL); - bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL); bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id); uint64_t get_current_comulative_blocksize_limit(); bool is_storing_blockchain(){return m_is_blockchain_storing;} uint64_t block_difficulty(size_t i); template - bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) - { + bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(const auto& bl_id, block_ids) - { - auto it = m_blocks_index.find(bl_id); - if(it == m_blocks_index.end()) + for (const auto& bl_id : block_ids) { + auto it = m_blockMap.find(bl_id); + if (it == m_blockMap.end()) { missed_bs.push_back(bl_id); - else - { + } else { CHECK_AND_ASSERT_MES(it->second < m_blocks.size(), false, "Internal error: bl_id=" << epee::string_tools::pod_to_hex(bl_id) << " have index record with offset="<second<< ", bigger then m_blocks.size()=" << m_blocks.size()); blocks.push_back(m_blocks[it->second].bl); } } + return true; } template - bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) - { + bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(const auto& tx_id, txs_ids) - { - auto it = m_transactions.find(tx_id); - if(it == m_transactions.end()) - { + for (const auto& tx_id : txs_ids) { + auto it = m_transactionMap.find(tx_id); + if (it == m_transactionMap.end()) { transaction tx; - if(!m_tx_pool.get_transaction(tx_id, tx)) + if (!m_tx_pool.get_transaction(tx_id, tx)) { missed_txs.push_back(tx_id); - else + } else { txs.push_back(tx); + } + } else { + txs.push_back(transactionByIndex(it->second).tx); } - else - txs.push_back(it->second.tx); } + return true; } + //debug functions void print_blockchain(uint64_t start_index, uint64_t end_index); void print_blockchain_index(); void print_blockchain_outs(const std::string& file); private: - typedef std::unordered_map blocks_by_id_index; - typedef std::unordered_map transactions_container; + struct Transaction { + transaction tx; + std::vector m_global_output_indexes; + + template void serialize(archive_t & ar, unsigned int version); + + BEGIN_SERIALIZE_OBJECT() + FIELD(tx) + FIELD(m_global_output_indexes) + END_SERIALIZE() + }; + + struct Block { + block bl; + uint32_t height; + uint64_t block_cumulative_size; + difficulty_type cumulative_difficulty; + uint64_t already_generated_coins; + std::vector transactions; + + template void serialize(Archive& archive, unsigned int version); + + BEGIN_SERIALIZE_OBJECT() + FIELD(bl) + VARINT_FIELD(height) + VARINT_FIELD(block_cumulative_size) + VARINT_FIELD(cumulative_difficulty) + VARINT_FIELD(already_generated_coins) + FIELD(transactions) + END_SERIALIZE() + }; + + struct TransactionIndex { + uint32_t block; + uint16_t transaction; + + template void serialize(Archive& archive, unsigned int version); + }; + typedef std::unordered_set key_images_container; - typedef std::vector blocks_container; - typedef std::unordered_map blocks_ext_by_hash; - typedef std::unordered_map blocks_by_hash; - typedef std::map>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction + typedef std::unordered_map blocks_ext_by_hash; + typedef std::map>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction tx_memory_pool& m_tx_pool; epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock - // main chain - blocks_container m_blocks; // height -> block_extended_info - blocks_by_id_index m_blocks_index; // crypto::hash -> height - transactions_container m_transactions; key_images_container m_spent_keys; size_t m_current_block_cumul_sz_limit; - - - // all alternative chains blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info - - // some invalid blocks - blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info outputs_container m_outputs; - std::string m_config_folder; checkpoints m_checkpoints; std::atomic m_is_in_checkpoint_zone; std::atomic m_is_blockchain_storing; - bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); - bool pop_block_from_blockchain(); - bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count); - bool purge_transaction_from_blockchain(const crypto::hash& tx_id); - bool purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check); + typedef SwappedVector Blocks; + typedef std::unordered_map BlockMap; + typedef std::unordered_map TransactionMap; - bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc); - bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc); + Blocks m_blocks; + BlockMap m_blockMap; + TransactionMap m_transactionMap; + + template bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); + bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc); - difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, block_extended_info& bei); + difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, Block& bei); bool prevalidate_miner_transaction(const block& b, uint64_t height); bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins); bool validate_transaction(const block& b, uint64_t height, const transaction& tx); bool rollback_blockchain_switching(std::list& original_chain, size_t rollback_height); - bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height); - bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector& global_indexes); - bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id); bool get_last_n_blocks_sizes(std::vector& sz, size_t count); - bool add_out_to_get_random_outs(std::vector >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i); + bool add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i); bool is_tx_spendtime_unlocked(uint64_t unlock_time); - bool add_block_as_invalid(const block& bl, const crypto::hash& h); - bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h); - size_t find_end_of_allowed_index(const std::vector >& amount_outs); + size_t find_end_of_allowed_index(const std::vector>& amount_outs); bool check_block_timestamp_main(const block& b); bool check_block_timestamp(std::vector timestamps, const block& b); uint64_t get_adjusted_time(); bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps); bool update_next_comulative_size_limit(); + bool find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset); + bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height = NULL); + bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL); + bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL); + bool have_tx_keyimg_as_spent(const crypto::key_image &key_im); + const Transaction& transactionByIndex(TransactionIndex index); + bool pushBlock(const block& blockData, block_verification_context& bvc); + bool pushBlock(Block& block); + void popBlock(const crypto::hash& blockHash); + bool pushTransaction(Block& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex); + void popTransaction(const transaction& transaction, const crypto::hash& transactionHash); + void popTransactions(const Block& block, const crypto::hash& minerTransactionHash); }; - /************************************************************************/ - /* */ - /************************************************************************/ - - #define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 12 - - template - void blockchain_storage::serialize(archive_t & ar, const unsigned int version) - { - if(version < 11) - return; - CRITICAL_REGION_LOCAL(m_blockchain_lock); - ar & m_blocks; - ar & m_blocks_index; - ar & m_transactions; - ar & m_spent_keys; - ar & m_alternative_chains; - ar & m_outputs; - ar & m_invalid_blocks; - ar & m_current_block_cumul_sz_limit; - /*serialization bug workaround*/ - if(version > 11) - { - uint64_t total_check_count = m_blocks.size() + m_blocks_index.size() + m_transactions.size() + m_spent_keys.size() + m_alternative_chains.size() + m_outputs.size() + m_invalid_blocks.size() + m_current_block_cumul_sz_limit; - if(archive_t::is_saving::value) - { - ar & total_check_count; - }else - { - uint64_t total_check_count_loaded = 0; - ar & total_check_count_loaded; - if(total_check_count != total_check_count_loaded) - { - LOG_ERROR("Blockchain storage data corruption detected. total_count loaded from file = " << total_check_count_loaded << ", expected = " << total_check_count); - - LOG_PRINT_L0("Blockchain storage:" << ENDL << - "m_blocks: " << m_blocks.size() << ENDL << - "m_blocks_index: " << m_blocks_index.size() << ENDL << - "m_transactions: " << m_transactions.size() << ENDL << - "m_spent_keys: " << m_spent_keys.size() << ENDL << - "m_alternative_chains: " << m_alternative_chains.size() << ENDL << - "m_outputs: " << m_outputs.size() << ENDL << - "m_invalid_blocks: " << m_invalid_blocks.size() << ENDL << - "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit); - - throw std::runtime_error("Blockchain data corruption"); - } - } - } - - - LOG_PRINT_L2("Blockchain storage:" << ENDL << - "m_blocks: " << m_blocks.size() << ENDL << - "m_blocks_index: " << m_blocks_index.size() << ENDL << - "m_transactions: " << m_transactions.size() << ENDL << - "m_spent_keys: " << m_spent_keys.size() << ENDL << - "m_alternative_chains: " << m_alternative_chains.size() << ENDL << - "m_outputs: " << m_outputs.size() << ENDL << - "m_invalid_blocks: " << m_invalid_blocks.size() << ENDL << - "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit); - } - - //------------------------------------------------------------------ - template - bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) - { + template bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); auto it = m_outputs.find(tx_in_to_key.amount); - if(it == m_outputs.end() || !tx_in_to_key.key_offsets.size()) + if (it == m_outputs.end() || !tx_in_to_key.key_offsets.size()) return false; std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets); - - - std::vector >& amount_outs_vec = it->second; + std::vector>& amount_outs_vec = it->second; size_t count = 0; - BOOST_FOREACH(uint64_t i, absolute_offsets) - { - if(i >= amount_outs_vec.size() ) - { + for (uint64_t i : absolute_offsets) { + if(i >= amount_outs_vec.size() ) { LOG_PRINT_L0("Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1); return false; } - transactions_container::iterator tx_it = m_transactions.find(amount_outs_vec[i].first); - CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "Wrong transaction id in output indexes: " << epee::string_tools::pod_to_hex(amount_outs_vec[i].first)); - CHECK_AND_ASSERT_MES(amount_outs_vec[i].second < tx_it->second.tx.vout.size(), false, - "Wrong index in transaction outputs: " << amount_outs_vec[i].second << ", expected less then " << tx_it->second.tx.vout.size()); - if(!vis.handle_output(tx_it->second.tx, tx_it->second.tx.vout[amount_outs_vec[i].second])) - { + + //auto tx_it = m_transactionMap.find(amount_outs_vec[i].first); + //CHECK_AND_ASSERT_MES(tx_it != m_transactionMap.end(), false, "Wrong transaction id in output indexes: " << epee::string_tools::pod_to_hex(amount_outs_vec[i].first)); + + const Transaction& tx = transactionByIndex(amount_outs_vec[i].first); + CHECK_AND_ASSERT_MES(amount_outs_vec[i].second < tx.tx.vout.size(), false, + "Wrong index in transaction outputs: " << amount_outs_vec[i].second << ", expected less then " << tx.tx.vout.size()); + if (!vis.handle_output(tx.tx, tx.tx.vout[amount_outs_vec[i].second])) { LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i); return false; } - if(count++ == absolute_offsets.size()-1 && pmax_related_block_height) - { - if(*pmax_related_block_height < tx_it->second.m_keeper_block_height) - *pmax_related_block_height = tx_it->second.m_keeper_block_height; + + if(count++ == absolute_offsets.size()-1 && pmax_related_block_height) { + if (*pmax_related_block_height < amount_outs_vec[i].first.block) { + *pmax_related_block_height = amount_outs_vec[i].first.block; + } } } @@ -322,6 +234,5 @@ namespace cryptonote } } - - -BOOST_CLASS_VERSION(cryptonote::blockchain_storage, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) +#include "cryptonote_boost_serialization.h" +#include "blockchain_storage_boost_serialization.h" diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h index 6aa06e87..3f0c94b3 100644 --- a/src/cryptonote_core/blockchain_storage_boost_serialization.h +++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once - +/* namespace boost { namespace serialization @@ -31,3 +31,4 @@ namespace boost } } +*/ diff --git a/src/cryptonote_core/checkpoints_create.h b/src/cryptonote_core/checkpoints_create.h index f4f3878e..60e20e24 100644 --- a/src/cryptonote_core/checkpoints_create.h +++ b/src/cryptonote_core/checkpoints_create.h @@ -26,6 +26,7 @@ namespace cryptonote { ADD_CHECKPOINT(468000, "251bcbd398b1f593193a7210934a3d87f692b2cb0c45206150f59683dd7e9ba1"); ADD_CHECKPOINT(480200, "363544ac9920c778b815c2fdbcbca70a0d79b21f662913a42da9b49e859f0e5b"); ADD_CHECKPOINT(484500, "5cdf2101a0a62a0ab2a1ca0c15a6212b21f6dbdc42a0b7c0bcf65ca40b7a14fb"); + ADD_CHECKPOINT(506000, "3d54c1132f503d98d3f0d78bb46a4503c1a19447cb348361a2232e241cb45a3c"); return true; } diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_core/cryptonote_basic_impl.cpp index 24b6b59d..348384c8 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.cpp +++ b/src/cryptonote_core/cryptonote_basic_impl.cpp @@ -34,7 +34,7 @@ namespace cryptonote { } //----------------------------------------------------------------------------------------------- bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward) { - uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> 18; + uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> EMISSION_SPEED_FACTOR; //make it soft if (median_size < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) { @@ -94,24 +94,17 @@ namespace cryptonote { return true; } //----------------------------------------------------------------------- - bool get_account_address_from_str(account_public_address& adr, const std::string& str) + bool get_account_address_from_str(uint64_t& prefix, account_public_address& adr, const std::string& str) { if (2 * sizeof(public_address_outer_blob) != str.size()) { blobdata data; - uint64_t prefix; if (!tools::base58::decode_addr(str, prefix, data)) { LOG_PRINT_L1("Invalid address format"); return false; } - if (CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX != prefix) - { - LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); - return false; - } - if (!::serialization::parse_binary(data, adr)) { LOG_PRINT_L1("Account public address keys can't be parsed"); @@ -127,6 +120,8 @@ namespace cryptonote { else { // Old address format + prefix = CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; + std::string buff; if(!string_tools::parse_hexstr_to_binbuff(str, buff)) return false; @@ -158,6 +153,22 @@ namespace cryptonote { return true; } + //----------------------------------------------------------------------- + bool get_account_address_from_str(account_public_address& adr, const std::string& str) + { + uint64_t prefix; + if(!get_account_address_from_str(prefix, adr, str)) + return false; + + if(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX != prefix) + { + LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); + return false; + } + + return true; + } + //----------------------------------------------------------------------- bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) { return cryptonote::get_transaction_hash(a) == cryptonote::get_transaction_hash(b); diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/cryptonote_core/cryptonote_basic_impl.h index cb6fb769..27251ef5 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.h +++ b/src/cryptonote_core/cryptonote_basic_impl.h @@ -41,6 +41,7 @@ namespace cryptonote { bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward); uint8_t get_account_address_checksum(const public_address_outer_blob& bl); std::string get_account_address_as_str(const account_public_address& adr); + bool get_account_address_from_str(uint64_t& prefix, account_public_address& adr, const std::string& str); bool get_account_address_from_str(account_public_address& adr, const std::string& str); bool is_coinbase(const transaction& tx); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index b6bfa09c..2e46c99b 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -283,10 +283,10 @@ namespace cryptonote return m_blockchain_storage.get_total_transactions(); } //----------------------------------------------------------------------------------------------- - bool core::get_outs(uint64_t amount, std::list& pkeys) - { - return m_blockchain_storage.get_outs(amount, pkeys); - } + //bool core::get_outs(uint64_t amount, std::list& pkeys) + //{ + // return m_blockchain_storage.get_outs(amount, pkeys); + //} //----------------------------------------------------------------------------------------------- bool core::add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block) { @@ -397,15 +397,10 @@ namespace cryptonote { m_miner.on_synchronized(); } - bool core::get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count) - { - return m_blockchain_storage.get_backward_blocks_sizes(from_height, sizes, count); - } - //----------------------------------------------------------------------------------------------- - bool core::add_new_block(const block& b, block_verification_context& bvc) - { - return m_blockchain_storage.add_new_block(b, bvc); - } + //bool core::get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count) + //{ + // return m_blockchain_storage.get_backward_blocks_sizes(from_height, sizes, count); + //} //----------------------------------------------------------------------------------------------- bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate) { @@ -417,7 +412,6 @@ namespace cryptonote return false; } - block b = AUTO_VAL_INIT(b); if(!parse_and_validate_block_from_blob(block_blob, b)) { @@ -425,9 +419,30 @@ namespace cryptonote bvc.m_verifivation_failed = true; return false; } - add_new_block(b, bvc); - if(update_miner_blocktemplate && bvc.m_added_to_main_chain) - update_miner_block_template(); + + m_blockchain_storage.add_new_block(b, bvc); + if (bvc.m_added_to_main_chain) { + cryptonote_connection_context exclude_context = boost::value_initialized(); + NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); + arg.hop = 0; + arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); + std::list missed_txs; + std::list txs; + m_blockchain_storage.get_transactions(b.tx_hashes, txs, missed_txs); + if (missed_txs.size() > 0 && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { + LOG_PRINT_L0("Block found but reorganize happened after that, block will not be relayed"); + } else { + CHECK_AND_ASSERT_MES(txs.size() == b.tx_hashes.size() && !missed_txs.size(), false, "cant find some transactions in found block:" << get_block_hash(b) << " txs.size()=" << txs.size() << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size()); + block_to_blob(b, arg.b.block); + BOOST_FOREACH(auto& tx, txs) arg.b.txs.push_back(t_serializable_object_to_blob(tx)); + m_pprotocol->relay_block(arg, exclude_context); + } + + if (update_miner_blocktemplate) { + update_miner_block_template(); + } + } + return true; } //----------------------------------------------------------------------------------------------- @@ -480,9 +495,9 @@ namespace cryptonote return m_blockchain_storage.get_block_by_hash(h, blk); } //----------------------------------------------------------------------------------------------- - void core::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { - m_blockchain_storage.get_all_known_block_ids(main, alt, invalid); - } + //void core::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { + // m_blockchain_storage.get_all_known_block_ids(main, alt, invalid); + //} //----------------------------------------------------------------------------------------------- std::string core::print_pool(bool short_format) { diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index cde52d5a..056aa074 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -59,7 +59,7 @@ namespace cryptonote bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs); bool get_transaction(const crypto::hash &h, transaction &tx); bool get_block_by_hash(const crypto::hash &h, block &blk); - void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); + //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); bool get_alternative_blocks(std::list& blocks); size_t get_alternative_blocks_count(); @@ -70,13 +70,13 @@ namespace cryptonote bool get_pool_transactions(std::list& txs); size_t get_pool_transactions_count(); size_t get_blockchain_total_transactions(); - bool get_outs(uint64_t amount, std::list& pkeys); + //bool get_outs(uint64_t amount, std::list& pkeys); bool have_block(const crypto::hash& id); bool get_short_chain_history(std::list& ids); bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); bool get_stat_info(core_stat_info& st_inf); - bool get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count); + //bool get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count); bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); crypto::hash get_tail_id(); bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); @@ -93,7 +93,6 @@ namespace cryptonote private: bool add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); bool add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block); - bool add_new_block(const block& b, block_verification_context& bvc); bool load_state_data(); bool parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob); diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 04958393..5b6fadf2 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -591,18 +591,23 @@ namespace cryptonote return get_object_hash(t, res, blob_size); } //--------------------------------------------------------------- - blobdata get_block_hashing_blob(const block& b) + bool get_block_hashing_blob(const block& b, blobdata& blob) { - blobdata blob = t_serializable_object_to_blob(static_cast(b)); + if(!t_serializable_object_to_blob(static_cast(b), blob)) + return false; crypto::hash tree_root_hash = get_tx_tree_hash(b); - blob.append((const char*)&tree_root_hash, sizeof(tree_root_hash )); - blob.append(tools::get_varint_data(b.tx_hashes.size()+1)); - return blob; + blob.append(reinterpret_cast(&tree_root_hash), sizeof(tree_root_hash)); + blob.append(tools::get_varint_data(b.tx_hashes.size() + 1)); + + return true; } //--------------------------------------------------------------- bool get_block_hash(const block& b, crypto::hash& res) { - return get_object_hash(get_block_hashing_blob(b), res); + blobdata blob; + if (!get_block_hashing_blob(b, blob)) + return false; + return get_object_hash(blob, res); } //--------------------------------------------------------------- crypto::hash get_block_hash(const block& b) @@ -641,8 +646,9 @@ namespace cryptonote //--------------------------------------------------------------- bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) { - block b_local = b; //workaround to avoid const errors with do_serialize - blobdata bd = get_block_hashing_blob(b); + blobdata bd; + if(!get_block_hashing_blob(b, bd)) + return false; crypto::cn_slow_hash(bd.data(), bd.size(), res); return true; } diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index 138fb522..662e160e 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -74,7 +74,7 @@ namespace cryptonote crypto::hash get_transaction_hash(const transaction& t); bool get_transaction_hash(const transaction& t, crypto::hash& res); bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size); - blobdata get_block_hashing_blob(const block& b); + bool get_block_hashing_blob(const block& b, blobdata& blob); bool get_block_hash(const block& b, crypto::hash& res); crypto::hash get_block_hash(const block& b); bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height); @@ -85,7 +85,6 @@ namespace cryptonote uint64_t get_outs_money_amount(const transaction& tx); bool check_inputs_types_supported(const transaction& tx); bool check_outs_valid(const transaction& tx); - blobdata get_block_hashing_blob(const block& b); bool parse_amount(uint64_t& amount, const std::string& str_amount); bool check_money_overflow(const transaction& tx); diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp index 56b459d6..fdc15457 100644 --- a/src/cryptonote_core/miner.cpp +++ b/src/cryptonote_core/miner.cpp @@ -31,7 +31,7 @@ namespace cryptonote { const command_line::arg_descriptor arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; const command_line::arg_descriptor arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; - const command_line::arg_descriptor arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; + const command_line::arg_descriptor arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; } @@ -340,7 +340,7 @@ namespace cryptonote crypto::hash h; get_block_longhash(b, h, height); - if(check_hash(h, local_diff)) + if(!m_stop && check_hash(h, local_diff)) { //we lucky! ++m_config.current_extra_message_index; @@ -354,6 +354,7 @@ namespace cryptonote epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); } } + nonce+=m_threads_total; ++m_hashes; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 916f10c3..5c60be0a 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -130,7 +130,7 @@ namespace nodetool if (!parse_peers_and_add_to_container(vm, arg_p2p_add_exclusive_node, m_exclusive_peers)) return false; } - else if (command_line::has_arg(vm, arg_p2p_add_priority_node)) + if (command_line::has_arg(vm, arg_p2p_add_priority_node)) { if (!parse_peers_and_add_to_container(vm, arg_p2p_add_priority_node, m_priority_peers)) return false; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index f9e04bb8..07f4a1cb 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -331,7 +331,7 @@ namespace cryptonote return false; } - if(req.reserve_size > 255) + if(req.reserve_size > TX_EXTRA_NONCE_MAX_COUNT) { error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE; error_resp.message = "To big reserved size, maximum 255"; @@ -357,31 +357,41 @@ namespace cryptonote LOG_ERROR("Failed to create block template"); return false; } + blobdata block_blob = t_serializable_object_to_blob(b); crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); if(tx_pub_key == null_pkey) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = "Internal error: failed to create block template"; - LOG_ERROR("Failed to tx pub key in coinbase extra"); + LOG_ERROR("Failed to find tx pub key in coinbase extra"); return false; } - res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); - if(!res.reserved_offset) + + if(0 < req.reserve_size) { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: failed to create block template"; - LOG_ERROR("Failed to find tx pub key in blockblob"); - return false; + res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); + if(!res.reserved_offset) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Internal error: failed to create block template"; + LOG_ERROR("Failed to find tx pub key in blockblob"); + return false; + } + res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + if(res.reserved_offset + req.reserve_size > block_blob.size()) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Internal error: failed to create block template"; + LOG_ERROR("Failed to calculate offset for reserved bytes"); + return false; + } } - res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) - if(res.reserved_offset + req.reserve_size > block_blob.size()) + else { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: failed to create block template"; - LOG_ERROR("Failed to calculate offset for "); - return false; + res.reserved_offset = 0; } + res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); res.status = CORE_RPC_STATUS_OK; return true; diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h index d06e8a22..00bb1741 100644 --- a/src/serialization/binary_utils.h +++ b/src/serialization/binary_utils.h @@ -2,6 +2,8 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once + #include #include "binary_archive.h" diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index b7763ffe..6e683e62 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -2,6 +2,8 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once + #include #include "serialization.h" diff --git a/src/serialization/json_utils.h b/src/serialization/json_utils.h index 35bcc033..24f5c11a 100644 --- a/src/serialization/json_utils.h +++ b/src/serialization/json_utils.h @@ -2,6 +2,8 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once + #include #include "json_archive.h" diff --git a/src/serialization/vector.h b/src/serialization/vector.h index 9a0c0ee5..d0747235 100644 --- a/src/serialization/vector.h +++ b/src/serialization/vector.h @@ -16,6 +16,13 @@ namespace serialization return ::do_serialize(ar, e); } + template + bool serialize_vector_element(Archive& ar, uint32_t& e) + { + ar.serialize_varint(e); + return true; + } + template bool serialize_vector_element(Archive& ar, uint64_t& e) { diff --git a/src/version.h.in b/src/version.h.in index 2d65ea8b..e412253e 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "0.8.9" +#define PROJECT_VERSION "0.8.10" #define PROJECT_VERSION_BUILD_NO "65" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 0d42dbaf..7189ca9f 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -55,6 +55,9 @@ namespace tools template struct wallet_error_base : public Base { + // This is necessary to compile with g++ 4.7.3, because of ~std::string() (m_loc) can throw an exception + ~wallet_error_base() throw() { } + const std::string& location() const { return m_loc; } std::string to_string() const @@ -96,6 +99,8 @@ namespace tools { } + ~failed_rpc_request() throw() { } + const std::string& status() const { return m_status; } std::string to_string() const @@ -128,6 +133,8 @@ namespace tools { } + ~unexpected_txin_type() throw() { } + const cryptonote::transaction& tx() const { return m_tx; } std::string to_string() const @@ -165,6 +172,8 @@ namespace tools { } + ~file_error_base() throw() { } + const std::string& file() const { return m_file; } std::string to_string() const { return wallet_logic_error::to_string(); } @@ -192,7 +201,7 @@ namespace tools struct refresh_error : public wallet_logic_error { protected: - refresh_error(std::string&& loc, const std::string& message) + explicit refresh_error(std::string&& loc, const std::string& message) : wallet_logic_error(std::move(loc), message) { } @@ -209,6 +218,8 @@ namespace tools { } + ~acc_outs_lookup_error() throw() { } + const cryptonote::transaction& tx() const { return m_tx; } const crypto::public_key& tx_pub_key() const { return m_tx_pub_key; } const cryptonote::account_keys& acc_keys() const { return m_acc_keys; } @@ -235,6 +246,8 @@ namespace tools { } + ~block_parse_error() throw() { } + const cryptonote::blobdata& block_blob() const { return m_block_blob; } std::string to_string() const { return refresh_error::to_string(); } @@ -255,6 +268,8 @@ namespace tools { } + ~tx_parse_error() throw() { } + const cryptonote::blobdata& tx_blob() const { return m_tx_blob; } std::string to_string() const { return refresh_error::to_string(); } @@ -266,7 +281,7 @@ namespace tools struct transfer_error : public wallet_logic_error { protected: - transfer_error(std::string&& loc, const std::string& message) + explicit transfer_error(std::string&& loc, const std::string& message) : wallet_logic_error(std::move(loc), message) { } @@ -276,7 +291,7 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct not_enough_money : public transfer_error { - not_enough_money(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee) + explicit not_enough_money(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee) : transfer_error(std::move(loc), "not enough money") , m_available(availbable) , m_tx_amount(tx_amount) @@ -315,6 +330,8 @@ namespace tools { } + ~not_enough_outs_to_mix() throw() { } + const scanty_outs_t& scanty_outs() const { return m_scanty_outs; } size_t mixin_count() const { return m_mixin_count; } @@ -347,6 +364,8 @@ namespace tools { } + ~tx_not_constructed() throw() { } + const sources_t& sources() const { return m_sources; } const destinations_t& destinations() const { return m_destinations; } uint64_t unlock_time() const { return m_unlock_time; } @@ -401,6 +420,8 @@ namespace tools { } + ~tx_rejected() throw() { } + const cryptonote::transaction& tx() const { return m_tx; } const std::string& status() const { return m_status; } @@ -420,13 +441,15 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct tx_sum_overflow : public transfer_error { - tx_sum_overflow(std::string&& loc, const std::vector& destinations, uint64_t fee) + explicit tx_sum_overflow(std::string&& loc, const std::vector& destinations, uint64_t fee) : transfer_error(std::move(loc), "transaction sum + fee exceeds " + cryptonote::print_money(std::numeric_limits::max())) , m_destinations(destinations) , m_fee(fee) { } + ~tx_sum_overflow() throw() { } + const std::vector& destinations() const { return m_destinations; } uint64_t fee() const { return m_fee; } @@ -457,6 +480,8 @@ namespace tools { } + ~tx_too_big() throw() { } + const cryptonote::transaction& tx() const { return m_tx; } uint64_t tx_size_limit() const { return m_tx_size_limit; } @@ -486,6 +511,8 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct wallet_rpc_error : public wallet_logic_error { + ~wallet_rpc_error() throw() { } + const std::string& request() const { return m_request; } std::string to_string() const @@ -496,7 +523,7 @@ namespace tools } protected: - wallet_rpc_error(std::string&& loc, const std::string& message, const std::string& request) + explicit wallet_rpc_error(std::string&& loc, const std::string& message, const std::string& request) : wallet_logic_error(std::move(loc), message) , m_request(request) { @@ -529,6 +556,8 @@ namespace tools { } + ~wallet_files_doesnt_correspond() throw() { } + const std::string& keys_file() const { return m_keys_file; } const std::string& wallet_file() const { return m_wallet_file; }