diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5e2d3c3a15..52bc52eac8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,8 +24,10 @@ if(MSVC)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/Platform/Windows)
elseif(APPLE)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/Platform/OSX)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/Platform/Posix)
else()
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/Platform/Linux)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/Platform/Posix)
endif()
set(STATIC ${MSVC} CACHE BOOL "Link libraries statically")
@@ -53,7 +55,7 @@ else()
endif()
set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Werror -Wno-error=extra -Wno-error=unused-function -Wno-error=deprecated-declarations -Wno-error=sign-compare -Wno-error=strict-aliasing -Wno-error=type-limits -Wno-unused-parameter -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized -Wno-error=unused-result")
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
- set(WARNINGS "${WARNINGS} -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration -Wno-error=unused-function")
+ set(WARNINGS "${WARNINGS} -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration -Wno-error=unused-function -Wno-error=missing-braces")
else()
set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized -Wno-error=clobbered -Wno-error=unused-but-set-variable")
endif()
diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt
index a48e57a80d..324f87fb8b 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.txt
@@ -1,3 +1,8 @@
+Release notes 1.0.11
+
+- New Bytecoin Wallet file format
+- Daemon loading optimization
+
Release notes 1.0.10
- Blocksize increase
diff --git a/include/IWallet.h b/include/IWallet.h
index e9f8eae194..4d5674499c 100755
--- a/include/IWallet.h
+++ b/include/IWallet.h
@@ -44,6 +44,12 @@ enum WalletEventType {
SYNC_COMPLETED,
};
+enum class WalletSaveLevel : uint8_t {
+ SAVE_KEYS_ONLY,
+ SAVE_KEYS_AND_TRANSACTIONS,
+ SAVE_ALL
+};
+
struct WalletTransactionCreatedData {
size_t transactionIndex;
};
@@ -126,13 +132,15 @@ class IWallet {
public:
virtual ~IWallet() {}
- virtual void initialize(const std::string& password) = 0;
- virtual void initializeWithViewKey(const Crypto::SecretKey& viewSecretKey, const std::string& password) = 0;
- virtual void load(std::istream& source, const std::string& password) = 0;
+ virtual void initialize(const std::string& path, const std::string& password) = 0;
+ virtual void initializeWithViewKey(const std::string& path, const std::string& password, const Crypto::SecretKey& viewSecretKey) = 0;
+ virtual void load(const std::string& path, const std::string& password, std::string& extra) = 0;
+ virtual void load(const std::string& path, const std::string& password) = 0;
virtual void shutdown() = 0;
virtual void changePassword(const std::string& oldPassword, const std::string& newPassword) = 0;
- virtual void save(std::ostream& destination, bool saveDetails = true, bool saveCache = true, bool encrypt = true) = 0;
+ virtual void save(WalletSaveLevel saveLevel = WalletSaveLevel::SAVE_ALL, const std::string& extra = "") = 0;
+ virtual void exportWallet(const std::string& path, bool encrypt = true, WalletSaveLevel saveLevel = WalletSaveLevel::SAVE_ALL, const std::string& extra = "") = 0;
virtual size_t getAddressCount() const = 0;
virtual std::string getAddress(size_t index) const = 0;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f6ea4393b7..dc02ea299e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -18,9 +18,9 @@ file(GLOB_RECURSE SimpleWallet SimpleWallet/*)
if(MSVC)
file(GLOB_RECURSE System System/* Platform/Windows/System/*)
elseif(APPLE)
-file(GLOB_RECURSE System System/* Platform/OSX/System/*)
+file(GLOB_RECURSE System System/* Platform/OSX/System/* Platform/Posix/System/*)
else()
-file(GLOB_RECURSE System System/* Platform/Linux/System/*)
+file(GLOB_RECURSE System System/* Platform/Linux/System/* Platform/Posix/System/*)
endif()
file(GLOB_RECURSE Transfers Transfers/*)
file(GLOB_RECURSE Wallet Wallet/*)
diff --git a/src/Common/FileMappedVector.cpp b/src/Common/FileMappedVector.cpp
new file mode 100644
index 0000000000..47f1d59e61
--- /dev/null
+++ b/src/Common/FileMappedVector.cpp
@@ -0,0 +1,22 @@
+// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers
+//
+// This file is part of Bytecoin.
+//
+// Bytecoin is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Bytecoin is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with Bytecoin. If not, see .
+
+#include "FileMappedVector.h"
+
+namespace {
+char suppressMSVCWarningLNK4221;
+}
diff --git a/src/Common/FileMappedVector.h b/src/Common/FileMappedVector.h
new file mode 100644
index 0000000000..c8a7da266e
--- /dev/null
+++ b/src/Common/FileMappedVector.h
@@ -0,0 +1,915 @@
+// Copyright (c) 2012-2016, The CryptoNote developers, The Bytecoin developers
+//
+// This file is part of Bytecoin.
+//
+// Bytecoin is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Bytecoin is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with Bytecoin. If not, see .
+
+#pragma once
+
+#include
+#include
+#include
+
+#include
+
+#include "System/MemoryMappedFile.h"
+
+#include "Common/ScopeExit.h"
+
+namespace Common {
+
+template
+struct EnableIfPod {
+ typedef typename std::enable_if::value, EnableIfPod>::type type;
+};
+
+enum class FileMappedVectorOpenMode {
+ OPEN,
+ CREATE,
+ OPEN_OR_CREATE
+};
+
+template
+class FileMappedVector : public EnableIfPod::type {
+public:
+ typedef T value_type;
+
+ const static uint64_t metadataSize = static_cast(2 * sizeof(uint64_t));
+ const static uint64_t valueSize = static_cast(sizeof(T));
+
+ class const_iterator {
+ public:
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef T value_type;
+ typedef ptrdiff_t difference_type;
+ typedef const T* pointer;
+ typedef const T& reference;
+
+ const_iterator() : m_fileMappedVector(nullptr) {
+ }
+
+ const_iterator(const FileMappedVector* fileMappedVector, size_t index) :
+ m_fileMappedVector(fileMappedVector),
+ m_index(index) {
+ }
+
+ const T& operator*() const {
+ return (*m_fileMappedVector)[m_index];
+ }
+
+ const T* operator->() const {
+ return &(*m_fileMappedVector)[m_index];
+ }
+
+ const_iterator& operator++() {
+ ++m_index;
+ return *this;
+ }
+
+ const_iterator operator++(int) {
+ const_iterator tmp = *this;
+ ++m_index;
+ return tmp;
+ }
+
+ const_iterator& operator--() {
+ --m_index;
+ return *this;
+ }
+
+ const_iterator operator--(int) {
+ const_iterator tmp = *this;
+ --m_index;
+ return tmp;
+ }
+
+ const_iterator& operator+=(difference_type n) {
+ m_index += n;
+ return *this;
+ }
+
+ const_iterator operator+(difference_type n) const {
+ return const_iterator(m_fileMappedVector, m_index + n);
+ }
+
+ friend const_iterator operator+(difference_type n, const const_iterator& i) {
+ return const_iterator(i.m_fileMappedVector, n + i.m_index);
+ }
+
+ const_iterator& operator-=(difference_type n) {
+ m_index -= n;
+ return *this;
+ }
+
+ const_iterator operator-(difference_type n) const {
+ return const_iterator(m_fileMappedVector, m_index - n);
+ }
+
+ difference_type operator-(const const_iterator& other) const {
+ return m_index - other.m_index;
+ }
+
+ const T& operator[](difference_type offset) const {
+ return (*m_fileMappedVector)[m_index + offset];
+ }
+
+ bool operator==(const const_iterator& other) const {
+ return m_index == other.m_index;
+ }
+
+ bool operator!=(const const_iterator& other) const {
+ return m_index != other.m_index;
+ }
+
+ bool operator<(const const_iterator& other) const {
+ return m_index < other.m_index;
+ }
+
+ bool operator>(const const_iterator& other) const {
+ return m_index > other.m_index;
+ }
+
+ bool operator<=(const const_iterator& other) const {
+ return m_index <= other.m_index;
+ }
+
+ bool operator>=(const const_iterator& other) const {
+ return m_index >= other.m_index;
+ }
+
+ size_t index() const {
+ return m_index;
+ }
+
+ protected:
+ const FileMappedVector* m_fileMappedVector;
+ size_t m_index;
+ };
+
+ class iterator : public const_iterator {
+ public:
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef T value_type;
+ typedef ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef T& reference;
+
+ iterator() : const_iterator() {
+ }
+
+ iterator(const FileMappedVector* fileMappedVector, size_t index) : const_iterator(fileMappedVector, index) {
+ }
+
+ T& operator*() const {
+ return const_cast((*const_iterator::m_fileMappedVector)[const_iterator::m_index]);
+ }
+
+ T* operator->() const {
+ return const_cast(&(*const_iterator::m_fileMappedVector)[const_iterator::m_index]);
+ }
+
+ iterator& operator++() {
+ ++const_iterator::m_index;
+ return *this;
+ }
+
+ iterator operator++(int) {
+ iterator tmp = *this;
+ ++const_iterator::m_index;
+ return tmp;
+ }
+
+ iterator& operator--() {
+ --const_iterator::m_index;
+ return *this;
+ }
+
+ iterator operator--(int) {
+ iterator tmp = *this;
+ --const_iterator::m_index;
+ return tmp;
+ }
+
+ iterator& operator+=(difference_type n) {
+ const_iterator::m_index += n;
+ return *this;
+ }
+
+ iterator operator+(difference_type n) const {
+ return iterator(const_iterator::m_fileMappedVector, const_iterator::m_index + n);
+ }
+
+ friend iterator operator+(difference_type n, const iterator& i) {
+ return iterator(i.m_fileMappedVector, n + i.m_index);
+ }
+
+ iterator& operator-=(difference_type n) {
+ const_iterator::m_index -= n;
+ return *this;
+ }
+
+ iterator operator-(difference_type n) const {
+ return iterator(const_iterator::m_fileMappedVector, const_iterator::m_index - n);
+ }
+
+ difference_type operator-(const iterator& other) const {
+ return const_iterator::m_index - other.m_index;
+ }
+
+ T& operator[](difference_type offset) const {
+ return (*const_iterator::m_fileMappedVector)[const_iterator::m_index + offset];
+ }
+ };
+
+ FileMappedVector();
+ FileMappedVector(const std::string& path, FileMappedVectorOpenMode mode = FileMappedVectorOpenMode::OPEN_OR_CREATE, uint64_t prefixSize = 0);
+ FileMappedVector(const FileMappedVector&) = delete;
+ FileMappedVector& operator=(const FileMappedVector&) = delete;
+
+ void open(const std::string& path, FileMappedVectorOpenMode mode = FileMappedVectorOpenMode::OPEN_OR_CREATE, uint64_t prefixSize = 0);
+ void close();
+ void close(std::error_code& ec);
+ bool isOpened() const;
+
+ bool empty() const;
+ uint64_t capacity() const;
+ uint64_t size() const;
+ void reserve(uint64_t n);
+ void shrink_to_fit();
+
+ const_iterator begin() const;
+ iterator begin();
+ const_iterator cbegin() const;
+ const_iterator end() const;
+ iterator end();
+ const_iterator cend() const;
+
+ const T& operator[](uint64_t index) const;
+ T& operator[](uint64_t index);
+ const T& at(uint64_t index) const;
+ T& at(uint64_t index);
+ const T& front() const;
+ T& front();
+ const T& back() const;
+ T& back();
+ const T* data() const;
+ T* data();
+
+ void clear();
+ iterator erase(const_iterator position);
+ iterator erase(const_iterator first, const_iterator last);
+ iterator insert(const_iterator position, const T& val);
+ template
+ iterator insert(const_iterator position, InputIterator first, InputIterator last);
+ void pop_back();
+ void push_back(const T& val);
+ void swap(FileMappedVector& other);
+
+ void flush();
+
+ const uint8_t* prefix() const;
+ uint8_t* prefix();
+ uint64_t prefixSize() const;
+ void resizePrefix(uint64_t newPrefixSize);
+
+ const uint8_t* suffix() const;
+ uint8_t* suffix();
+ uint64_t suffixSize() const;
+ void resizeSuffix(uint64_t newSuffixSize);
+
+ void rename(const std::string& newPath, std::error_code& ec);
+ void rename(const std::string& newPath);
+
+ template
+ void atomicUpdate(F&& func);
+
+private:
+ std::string m_path;
+ System::MemoryMappedFile m_file;
+ uint64_t m_prefixSize;
+ uint64_t m_suffixSize;
+
+private:
+ template
+ void atomicUpdate(uint64_t newSize, uint64_t newCapacity, uint64_t newPrefixSize, uint64_t newSuffixSize, F&& func);
+ template
+ void atomicUpdate0(uint64_t newCapacity, uint64_t newPrefixSize, uint64_t newSuffixSize, F&& func);
+
+ void open(const std::string& path, uint64_t prefixSize);
+ void create(const std::string& path, uint64_t initialCapacity, uint64_t prefixSize, uint64_t suffixSize);
+
+ uint8_t* prefixPtr();
+ const uint8_t* prefixPtr() const;
+ uint64_t* capacityPtr();
+ const uint64_t* capacityPtr() const;
+ const uint64_t* sizePtr() const;
+ uint64_t* sizePtr();
+ T* vectorDataPtr();
+ const T* vectorDataPtr() const;
+ uint8_t* suffixPtr();
+ const uint8_t* suffixPtr() const;
+
+ uint64_t vectorDataSize();
+
+ uint64_t nextCapacity();
+
+ void flushElement(uint64_t index);
+ void flushSize();
+};
+
+template
+FileMappedVector::FileMappedVector() {
+}
+
+template
+FileMappedVector::FileMappedVector(const std::string& path, FileMappedVectorOpenMode mode, uint64_t prefixSize) {
+ open(path, mode, prefixSize);
+}
+
+template
+void FileMappedVector::open(const std::string& path, FileMappedVectorOpenMode mode, uint64_t prefixSize) {
+ assert(!isOpened());
+
+ const uint64_t initialCapacity = 10;
+
+ boost::filesystem::path filePath = path;
+ boost::filesystem::path bakPath = path + ".bak";
+ bool fileExists;
+ if (boost::filesystem::exists(filePath)) {
+ if (boost::filesystem::exists(bakPath)) {
+ boost::filesystem::remove(bakPath);
+ }
+
+ fileExists = true;
+ } else if (boost::filesystem::exists(bakPath)) {
+ boost::filesystem::rename(bakPath, filePath);
+ fileExists = true;
+ } else {
+ fileExists = false;
+ }
+
+ if (mode == FileMappedVectorOpenMode::OPEN) {
+ open(path, prefixSize);
+ } else if (mode == FileMappedVectorOpenMode::CREATE) {
+ create(path, initialCapacity, prefixSize, 0);
+ } else if (mode == FileMappedVectorOpenMode::OPEN_OR_CREATE) {
+ if (fileExists) {
+ open(path, prefixSize);
+ } else {
+ create(path, initialCapacity, prefixSize, 0);
+ }
+ } else {
+ throw std::runtime_error("FileMappedVector: Unsupported open mode: " + std::to_string(static_cast(mode)));
+ }
+}
+
+template
+void FileMappedVector::close(std::error_code& ec) {
+ m_file.close(ec);
+ if (!ec) {
+ m_prefixSize = 0;
+ m_suffixSize = 0;
+ m_path.clear();
+ }
+}
+
+template
+void FileMappedVector::close() {
+ std::error_code ec;
+ close(ec);
+ if (ec) {
+ throw std::system_error(ec, "FileMappedVector::close");
+ }
+}
+
+template
+bool FileMappedVector::isOpened() const {
+ return m_file.isOpened();
+}
+
+template
+bool FileMappedVector::empty() const {
+ assert(isOpened());
+
+ return size() == 0;
+}
+
+template
+uint64_t FileMappedVector::capacity() const {
+ assert(isOpened());
+
+ return *capacityPtr();
+}
+
+template
+uint64_t FileMappedVector::size() const {
+ assert(isOpened());
+
+ return *sizePtr();
+}
+
+template
+void FileMappedVector::reserve(uint64_t n) {
+ assert(isOpened());
+
+ if (n > capacity()) {
+ atomicUpdate(size(), n, prefixSize(), suffixSize(), [this](value_type* target) {
+ std::copy(cbegin(), cend(), target);
+ });
+ }
+}
+
+template
+void FileMappedVector::shrink_to_fit() {
+ assert(isOpened());
+
+ if (size() < capacity()) {
+ atomicUpdate(size(), size(), prefixSize(), suffixSize(), [this](value_type* target) {
+ std::copy(cbegin(), cend(), target);
+ });
+ }
+}
+
+template
+typename FileMappedVector::iterator FileMappedVector::begin() {
+ assert(isOpened());
+
+ return iterator(this, 0);
+}
+
+template
+typename FileMappedVector::const_iterator FileMappedVector::begin() const {
+ assert(isOpened());
+
+ return const_iterator(this, 0);
+}
+
+template
+typename FileMappedVector::const_iterator FileMappedVector::cbegin() const {
+ assert(isOpened());
+
+ return const_iterator(this, 0);
+}
+
+template
+typename FileMappedVector::const_iterator FileMappedVector::end() const {
+ assert(isOpened());
+
+ return const_iterator(this, size());
+}
+
+template
+typename FileMappedVector::iterator FileMappedVector::end() {
+ assert(isOpened());
+
+ return iterator(this, size());
+}
+
+template
+typename FileMappedVector::const_iterator FileMappedVector::cend() const {
+ assert(isOpened());
+
+ return const_iterator(this, size());
+}
+
+template
+const T& FileMappedVector::operator[](uint64_t index) const {
+ assert(isOpened());
+
+ return vectorDataPtr()[index];
+}
+
+template
+T& FileMappedVector::operator[](uint64_t index) {
+ assert(isOpened());
+
+ return vectorDataPtr()[index];
+}
+
+template
+const T& FileMappedVector::at(uint64_t index) const {
+ assert(isOpened());
+
+ if (index >= size()) {
+ throw std::out_of_range("FileMappedVector::at " + std::to_string(index));
+ }
+
+ return vectorDataPtr()[index];
+}
+
+template
+T& FileMappedVector::at(uint64_t index) {
+ assert(isOpened());
+
+ if (index >= size()) {
+ throw std::out_of_range("FileMappedVector::at " + std::to_string(index));
+ }
+
+ return vectorDataPtr()[index];
+}
+
+template
+const T& FileMappedVector::front() const {
+ assert(isOpened());
+
+ return vectorDataPtr()[0];
+}
+
+template
+T& FileMappedVector::front() {
+ assert(isOpened());
+
+ return vectorDataPtr()[0];
+}
+
+template
+const T& FileMappedVector::back() const {
+ assert(isOpened());
+
+ return vectorDataPtr()[size() - 1];
+}
+
+template
+T& FileMappedVector::back() {
+ assert(isOpened());
+
+ return vectorDataPtr()[size() - 1];
+}
+
+template
+const T* FileMappedVector::data() const {
+ assert(isOpened());
+
+ return vectorDataPtr();
+}
+
+template
+T* FileMappedVector::data() {
+ assert(isOpened());
+
+ return vectorDataPtr();
+}
+
+template
+void FileMappedVector::clear() {
+ assert(isOpened());
+
+ *sizePtr() = 0;
+ flushSize();
+}
+
+template
+typename FileMappedVector::iterator FileMappedVector::erase(const_iterator position) {
+ assert(isOpened());
+
+ return erase(position, std::next(position));
+}
+
+template
+typename FileMappedVector::iterator FileMappedVector::erase(const_iterator first, const_iterator last) {
+ assert(isOpened());
+
+ uint64_t newSize = size() - std::distance(first, last);
+
+ atomicUpdate(newSize, capacity(), prefixSize(), suffixSize(), [this, first, last](value_type* target) {
+ std::copy(cbegin(), first, target);
+ std::copy(last, cend(), target + std::distance(cbegin(), first));
+ });
+
+ return iterator(this, first.index());
+}
+
+template
+typename FileMappedVector::iterator FileMappedVector::insert(const_iterator position, const T& val) {
+ assert(isOpened());
+
+ return insert(position, &val, &val + 1);
+}
+
+template
+template
+typename FileMappedVector::iterator FileMappedVector::insert(const_iterator position, InputIterator first, InputIterator last) {
+ assert(isOpened());
+
+ uint64_t newSize = size() + static_cast(std::distance(first, last));
+ uint64_t newCapacity;
+ if (newSize > capacity()) {
+ newCapacity = nextCapacity();
+ if (newSize > newCapacity) {
+ newCapacity = newSize;
+ }
+ } else {
+ newCapacity = capacity();
+ }
+
+ atomicUpdate(newSize, newCapacity, prefixSize(), suffixSize(), [this, position, first, last](value_type* target) {
+ std::copy(cbegin(), position, target);
+ std::copy(first, last, target + position.index());
+ std::copy(position, cend(), target + position.index() + std::distance(first, last));
+ });
+
+ return iterator(this, position.index());
+}
+
+template
+void FileMappedVector::pop_back() {
+ assert(isOpened());
+
+ --(*sizePtr());
+ flushSize();
+}
+
+template
+void FileMappedVector::push_back(const T& val) {
+ assert(isOpened());
+
+ if (capacity() == size()) {
+ reserve(nextCapacity());
+ }
+
+ vectorDataPtr()[size()] = val;
+ flushElement(size());
+
+ ++(*sizePtr());
+ flushSize();
+}
+
+template
+void FileMappedVector::swap(FileMappedVector& other) {
+ m_path.swap(other.m_path);
+ m_file.swap(other.m_file);
+ std::swap(m_prefixSize, other.m_prefixSize);
+ std::swap(m_suffixSize, other.m_suffixSize);
+}
+
+template
+void FileMappedVector::flush() {
+ assert(isOpened());
+
+ m_file.flush(m_file.data(), m_file.size());
+}
+
+template
+const uint8_t* FileMappedVector::prefix() const {
+ assert(isOpened());
+
+ return prefixPtr();
+}
+
+template
+uint8_t* FileMappedVector::prefix() {
+ assert(isOpened());
+
+ return prefixPtr();
+}
+
+template
+uint64_t FileMappedVector::prefixSize() const {
+ assert(isOpened());
+
+ return m_prefixSize;
+}
+
+template
+void FileMappedVector::resizePrefix(uint64_t newPrefixSize) {
+ assert(isOpened());
+
+ if (prefixSize() != newPrefixSize) {
+ atomicUpdate(size(), capacity(), newPrefixSize, suffixSize(), [this](value_type* target) {
+ std::copy(cbegin(), cend(), target);
+ });
+ }
+}
+
+template
+const uint8_t* FileMappedVector::suffix() const {
+ assert(isOpened());
+
+ return suffixPtr();
+}
+
+template
+uint8_t* FileMappedVector::suffix() {
+ assert(isOpened());
+
+ return suffixPtr();
+}
+
+template
+uint64_t FileMappedVector::suffixSize() const {
+ assert(isOpened());
+
+ return m_suffixSize;
+}
+
+template
+void FileMappedVector::resizeSuffix(uint64_t newSuffixSize) {
+ assert(isOpened());
+
+ if (suffixSize() != newSuffixSize) {
+ atomicUpdate(size(), capacity(), prefixSize(), newSuffixSize, [this](value_type* target) {
+ std::copy(cbegin(), cend(), target);
+ });
+ }
+}
+
+template
+void FileMappedVector::rename(const std::string& newPath, std::error_code& ec) {
+ m_file.rename(newPath, ec);
+ if (!ec) {
+ m_path = newPath;
+ }
+}
+
+template
+void FileMappedVector::rename(const std::string& newPath) {
+ m_file.rename(newPath);
+ m_path = newPath;
+}
+
+template
+template
+void FileMappedVector::atomicUpdate(F&& func) {
+ atomicUpdate0(capacity(), prefixSize(), suffixSize(), std::move(func));
+}
+
+template
+template
+void FileMappedVector::atomicUpdate(uint64_t newSize, uint64_t newCapacity, uint64_t newPrefixSize, uint64_t newSuffixSize, F&& func) {
+ assert(newSize <= newCapacity);
+
+ atomicUpdate0(newCapacity, newPrefixSize, newSuffixSize, [this, newSize, &func](FileMappedVector& newVector) {
+ if (prefixSize() != 0 && newVector.prefixSize() != 0) {
+ std::copy(prefixPtr(), prefixPtr() + std::min(prefixSize(), newVector.prefixSize()), newVector.prefix());
+ }
+
+ *newVector.sizePtr() = newSize;
+ func(newVector.data());
+
+ if (suffixSize() != 0 && newVector.suffixSize() != 0) {
+ std::copy(suffixPtr(), suffixPtr() + std::min(suffixSize(), newVector.suffixSize()), newVector.suffix());
+ }
+ });
+}
+
+template
+template
+void FileMappedVector::atomicUpdate0(uint64_t newCapacity, uint64_t newPrefixSize, uint64_t newSuffixSize, F&& func) {
+ if (m_file.path() != m_path) {
+ throw std::runtime_error("Vector is mapped to a .bak file due to earlier errors");
+ }
+
+ boost::filesystem::path bakPath = m_path + ".bak";
+ boost::filesystem::path tmpPath = boost::filesystem::unique_path(m_path + ".tmp.%%%%-%%%%");
+
+ if (boost::filesystem::exists(bakPath)) {
+ boost::filesystem::remove(bakPath);
+ }
+
+ Tools::ScopeExit tmpFileDeleter([&tmpPath] {
+ boost::system::error_code ignore;
+ boost::filesystem::remove(tmpPath, ignore);
+ });
+
+ // Copy file. It is slow but atomic operation
+ FileMappedVector tmpVector;
+ tmpVector.create(tmpPath.string(), newCapacity, newPrefixSize, newSuffixSize);
+ func(tmpVector);
+ tmpVector.flush();
+
+ // Swap files
+ std::error_code ec;
+ std::error_code ignore;
+ m_file.rename(bakPath.string());
+ tmpVector.rename(m_path, ec);
+ if (ec) {
+ // Try to restore and ignore errors
+ m_file.rename(m_path, ignore);
+ throw std::system_error(ec, "Failed to swap temporary and vector files");
+ }
+
+ m_path = bakPath.string();
+ swap(tmpVector);
+ tmpFileDeleter.cancel();
+
+ // Remove .bak file and ignore errors
+ tmpVector.close(ignore);
+ boost::system::error_code boostError;
+ boost::filesystem::remove(bakPath, boostError);
+}
+
+template
+void FileMappedVector::open(const std::string& path, uint64_t prefixSize) {
+ m_prefixSize = prefixSize;
+ m_file.open(path);
+ m_path = path;
+
+ if (m_file.size() < prefixSize + metadataSize) {
+ throw std::runtime_error("FileMappedVector::open() file is too small");
+ }
+
+ if (size() > capacity()) {
+ throw std::runtime_error("FileMappedVector::open() vector size is greater than capacity");
+ }
+
+ auto minRequiredFileSize = m_prefixSize + metadataSize + vectorDataSize();
+ if (m_file.size() < minRequiredFileSize) {
+ throw std::runtime_error("FileMappedVector::open() invalid file size");
+ }
+
+ m_suffixSize = m_file.size() - minRequiredFileSize;
+}
+
+template
+void FileMappedVector::create(const std::string& path, uint64_t initialCapacity, uint64_t prefixSize, uint64_t suffixSize) {
+ m_file.create(path, prefixSize + metadataSize + initialCapacity * valueSize + suffixSize, false);
+ m_path = path;
+ m_prefixSize = prefixSize;
+ m_suffixSize = suffixSize;
+ *sizePtr() = 0;
+ *capacityPtr() = initialCapacity;
+ m_file.flush(reinterpret_cast(sizePtr()), metadataSize);
+}
+
+template
+uint8_t* FileMappedVector::prefixPtr() {
+ return m_file.data();
+}
+
+template
+const uint8_t* FileMappedVector::prefixPtr() const {
+ return m_file.data();
+}
+
+template
+uint64_t* FileMappedVector::capacityPtr() {
+ return reinterpret_cast(prefixPtr() + m_prefixSize);
+}
+
+template
+const uint64_t* FileMappedVector::capacityPtr() const {
+ return reinterpret_cast(prefixPtr() + m_prefixSize);
+}
+
+template
+const uint64_t* FileMappedVector::sizePtr() const {
+ return capacityPtr() + 1;
+}
+
+template
+uint64_t* FileMappedVector::sizePtr() {
+ return capacityPtr() + 1;
+}
+
+template
+T* FileMappedVector::vectorDataPtr() {
+ return reinterpret_cast(sizePtr() + 1);
+}
+
+template
+const T* FileMappedVector::vectorDataPtr() const {
+ return reinterpret_cast(sizePtr() + 1);
+}
+
+template
+uint8_t* FileMappedVector::suffixPtr() {
+ return reinterpret_cast(vectorDataPtr() + capacity());
+}
+
+template
+const uint8_t* FileMappedVector::suffixPtr() const {
+ return reinterpret_cast(vectorDataPtr() + capacity());
+}
+
+template
+uint64_t FileMappedVector::vectorDataSize() {
+ return capacity() * valueSize;
+}
+
+template
+uint64_t FileMappedVector::nextCapacity() {
+ return capacity() + capacity() / 2 + 1;
+}
+
+template
+void FileMappedVector::flushElement(uint64_t index) {
+ m_file.flush(reinterpret_cast(vectorDataPtr() + index), valueSize);
+}
+
+template
+void FileMappedVector::flushSize() {
+ m_file.flush(reinterpret_cast(sizePtr()), sizeof(uint64_t));
+}
+
+}
diff --git a/src/CryptoNoteConfig.h b/src/CryptoNoteConfig.h
index 9d1551662e..7bd5de508f 100644
--- a/src/CryptoNoteConfig.h
+++ b/src/CryptoNoteConfig.h
@@ -68,11 +68,11 @@ const size_t FUSION_TX_MAX_SIZE = CRYPTONOTE_BLOCK_
const size_t FUSION_TX_MIN_INPUT_COUNT = 12;
const size_t FUSION_TX_MIN_IN_OUT_COUNT_RATIO = 4;
-const uint64_t UPGRADE_HEIGHT_V2 = 546602;
-const uint64_t UPGRADE_HEIGHT_V3 = 985548;
+const uint32_t UPGRADE_HEIGHT_V2 = 546602;
+const uint32_t UPGRADE_HEIGHT_V3 = 985548;
const unsigned UPGRADE_VOTING_THRESHOLD = 90; // percent
-const size_t UPGRADE_VOTING_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks
-const size_t UPGRADE_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks
+const uint32_t UPGRADE_VOTING_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks
+const uint32_t UPGRADE_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks
static_assert(0 < UPGRADE_VOTING_THRESHOLD && UPGRADE_VOTING_THRESHOLD <= 100, "Bad UPGRADE_VOTING_THRESHOLD");
static_assert(UPGRADE_VOTING_WINDOW > 1, "Bad UPGRADE_VOTING_WINDOW");
@@ -175,7 +175,8 @@ const CheckpointData CHECKPOINTS[] = {
{979000, "d8290eb4eedbe638f5dbadebcaf3ea434857ce96168185dc04f75b6cc1f4fda6"},
{985548, "8d53e0d97594755a621feaee0978c0431fc01f42b85ff76a03af8641e2009d57"},
{985549, "dc6f8d9319282475c981896b98ff9772ae2499533c2302c32faf65115aaf2554"},
- {996000, "c9a9243049acc7773a3e58ae354d66f8ea83996ece93ffbaad0b8b42b5fb7223"}
+ {996000, "c9a9243049acc7773a3e58ae354d66f8ea83996ece93ffbaad0b8b42b5fb7223"},
+ {1021000, "a0c4107d327ffeb31dabe135a7124191b0a5ef7c4fa34f06babc1f0546ab938e"}
};
} // CryptoNote
diff --git a/src/CryptoNoteCore/Blockchain.cpp b/src/CryptoNoteCore/Blockchain.cpp
index f318201ffe..cdfdab964b 100644
--- a/src/CryptoNoteCore/Blockchain.cpp
+++ b/src/CryptoNoteCore/Blockchain.cpp
@@ -465,11 +465,38 @@ bool Blockchain::init(const std::string& config_folder, bool load_existing) {
}
}
+ uint32_t lastValidCheckpointHeight = 0;
+ if (!checkCheckpoints(lastValidCheckpointHeight)) {
+ logger(WARNING, BRIGHT_YELLOW) << "Invalid checkpoint found. Rollback blockchain to height=" << lastValidCheckpointHeight;
+ rollbackBlockchainTo(lastValidCheckpointHeight);
+ }
+
if (!m_upgradeDetectorV2.init() || !m_upgradeDetectorV3.init()) {
logger(ERROR, BRIGHT_RED) << "Failed to initialize upgrade detector";
return false;
}
+ bool reinitUpgradeDetectors = false;
+ if (!checkUpgradeHeight(m_upgradeDetectorV2)) {
+ uint32_t upgradeHeight = m_upgradeDetectorV2.upgradeHeight();
+ assert(upgradeHeight != UpgradeDetectorBase::UNDEF_HEIGHT);
+ logger(WARNING, BRIGHT_YELLOW) << "Invalid block version at " << upgradeHeight + 1 << ": real=" << static_cast(m_blocks[upgradeHeight + 1].bl.majorVersion) <<
+ " expected=" << static_cast(m_upgradeDetectorV2.targetVersion()) << ". Rollback blockchain to height=" << upgradeHeight;
+ rollbackBlockchainTo(upgradeHeight);
+ reinitUpgradeDetectors = true;
+ } else if (!checkUpgradeHeight(m_upgradeDetectorV3)) {
+ uint32_t upgradeHeight = m_upgradeDetectorV3.upgradeHeight();
+ logger(WARNING, BRIGHT_YELLOW) << "Invalid block version at " << upgradeHeight + 1 << ": real=" << static_cast(m_blocks[upgradeHeight + 1].bl.majorVersion) <<
+ " expected=" << static_cast(m_upgradeDetectorV3.targetVersion()) << ". Rollback blockchain to height=" << upgradeHeight;
+ rollbackBlockchainTo(upgradeHeight);
+ reinitUpgradeDetectors = true;
+ }
+
+ if (reinitUpgradeDetectors && (!m_upgradeDetectorV2.init() || !m_upgradeDetectorV3.init())) {
+ logger(ERROR, BRIGHT_RED) << "Failed to initialize upgrade detector";
+ return false;
+ }
+
update_next_comulative_size_limit();
uint64_t timestamp_diff = time(NULL) - m_blocks.back().bl.timestamp;
@@ -700,7 +727,7 @@ bool Blockchain::rollback_blockchain_switching(std::list &original_chain,
std::lock_guard lk(m_blockchain_lock);
// remove failed subchain
for (size_t i = m_blocks.size() - 1; i >= rollback_height; i--) {
- popBlock(get_block_hash(m_blocks.back().bl));
+ popBlock();
}
// return back original chain
@@ -738,7 +765,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list disconnected_chain;
for (size_t i = m_blocks.size() - 1; i >= split_height; i--) {
Block b = m_blocks[i].bl;
- popBlock(get_block_hash(b));
+ popBlock();
//if (!(r)) { logger(ERROR, BRIGHT_RED) << "failed to remove block on chain switching"; return false; }
disconnected_chain.push_front(b);
}
@@ -773,9 +800,8 @@ bool Blockchain::switch_to_alternative_blockchain(std::list();
bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc, false);
if (!r) {
- logger(ERROR, BRIGHT_RED) << ("Failed to push ex-main chain blocks to alternative chain ");
- rollback_blockchain_switching(disconnected_chain, split_height);
- return false;
+ logger(WARNING, BRIGHT_YELLOW) << ("Failed to push ex-main chain blocks to alternative chain ");
+ break;
}
}
}
@@ -1910,7 +1936,7 @@ bool Blockchain::pushBlock(BlockEntry& block) {
return true;
}
-void Blockchain::popBlock(const Crypto::Hash& blockHash) {
+void Blockchain::popBlock() {
if (m_blocks.empty()) {
logger(ERROR, BRIGHT_RED) <<
"Attempt to pop block from empty blockchain.";
@@ -1923,16 +1949,7 @@ void Blockchain::popBlock(const Crypto::Hash& blockHash) {
}
saveTransactions(transactions);
-
- popTransactions(m_blocks.back(), getObjectHash(m_blocks.back().bl.baseTransaction));
-
- m_timestampIndex.remove(m_blocks.back().bl.timestamp, blockHash);
- m_generatedTransactionsIndex.remove(m_blocks.back().bl);
-
- m_blocks.pop_back();
- m_blockIndex.pop();
-
- assert(m_blockIndex.size() == m_blocks.size());
+ removeLastBlock();
m_upgradeDetectorV2.blockPopped();
m_upgradeDetectorV3.blockPopped();
@@ -2164,6 +2181,61 @@ bool Blockchain::validateInput(const MultisignatureInput& input, const Crypto::H
return true;
}
+bool Blockchain::checkCheckpoints(uint32_t& lastValidCheckpointHeight) {
+ std::vector checkpointHeights = m_checkpoints.getCheckpointHeights();
+ for (const auto& checkpointHeight : checkpointHeights) {
+ if (m_blocks.size() <= checkpointHeight) {
+ return true;
+ }
+
+ if(m_checkpoints.check_block(checkpointHeight, getBlockIdByHeight(checkpointHeight))) {
+ lastValidCheckpointHeight = checkpointHeight;
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void Blockchain::rollbackBlockchainTo(uint32_t height) {
+ while (height + 1 < m_blocks.size()) {
+ removeLastBlock();
+ }
+}
+
+void Blockchain::removeLastBlock() {
+ if (m_blocks.empty()) {
+ logger(ERROR, BRIGHT_RED) <<
+ "Attempt to pop block from empty blockchain.";
+ return;
+ }
+
+ logger(DEBUGGING) << "Removing last block with height " << m_blocks.back().height;
+ popTransactions(m_blocks.back(), getObjectHash(m_blocks.back().bl.baseTransaction));
+
+ Crypto::Hash blockHash = getBlockIdByHeight(m_blocks.back().height);
+ m_timestampIndex.remove(m_blocks.back().bl.timestamp, blockHash);
+ m_generatedTransactionsIndex.remove(m_blocks.back().bl);
+
+ m_blocks.pop_back();
+ m_blockIndex.pop();
+
+ assert(m_blockIndex.size() == m_blocks.size());
+}
+
+bool Blockchain::checkUpgradeHeight(const UpgradeDetector& upgradeDetector) {
+ uint32_t upgradeHeight = upgradeDetector.upgradeHeight();
+ if (upgradeHeight != UpgradeDetectorBase::UNDEF_HEIGHT && upgradeHeight + 1 < m_blocks.size()) {
+ logger(INFO) << "Checking block version at " << upgradeHeight + 1;
+ if (m_blocks[upgradeHeight + 1].bl.majorVersion != upgradeDetector.targetVersion()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool Blockchain::getLowerBound(uint64_t timestamp, uint64_t startOffset, uint32_t& height) {
std::lock_guard lk(m_blockchain_lock);
@@ -2350,7 +2422,7 @@ void Blockchain::saveTransactions(const std::vector& transactions)
tx_verification_context context;
for (size_t i = 0; i < transactions.size(); ++i) {
if (!m_tx_pool.add_tx(transactions[transactions.size() - 1 - i], context, true)) {
- throw std::runtime_error("Blockchain::saveTransactions, failed to add transaction to pool");
+ logger(WARNING, BRIGHT_YELLOW) << "Blockchain::saveTransactions, failed to add transaction to pool";
}
}
}
diff --git a/src/CryptoNoteCore/Blockchain.h b/src/CryptoNoteCore/Blockchain.h
index 132cf1c11f..53e0ab8ec8 100755
--- a/src/CryptoNoteCore/Blockchain.h
+++ b/src/CryptoNoteCore/Blockchain.h
@@ -303,11 +303,15 @@ namespace CryptoNote {
bool pushBlock(const Block& blockData, block_verification_context& bvc);
bool pushBlock(const Block& blockData, const std::vector& transactions, block_verification_context& bvc);
bool pushBlock(BlockEntry& block);
- void popBlock(const Crypto::Hash& blockHash);
+ void popBlock();
bool pushTransaction(BlockEntry& block, const Crypto::Hash& transactionHash, TransactionIndex transactionIndex);
void popTransaction(const Transaction& transaction, const Crypto::Hash& transactionHash);
void popTransactions(const BlockEntry& block, const Crypto::Hash& minerTransactionHash);
bool validateInput(const MultisignatureInput& input, const Crypto::Hash& transactionHash, const Crypto::Hash& transactionPrefixHash, const std::vector& transactionSignatures);
+ bool checkCheckpoints(uint32_t& lastValidCheckpointHeight);
+ void rollbackBlockchainTo(uint32_t height);
+ void removeLastBlock();
+ bool checkUpgradeHeight(const UpgradeDetector& upgradeDetector);
bool storeBlockchainIndices();
bool loadBlockchainIndices();
diff --git a/src/CryptoNoteCore/BlockchainIndices.cpp b/src/CryptoNoteCore/BlockchainIndices.cpp
index 39fa83cba0..0b9f253286 100755
--- a/src/CryptoNoteCore/BlockchainIndices.cpp
+++ b/src/CryptoNoteCore/BlockchainIndices.cpp
@@ -25,7 +25,11 @@
namespace CryptoNote {
-PaymentIdIndex::PaymentIdIndex(bool _enabled) : enabled(_enabled) {
+namespace {
+ const size_t DEFAULT_BUCKET_COUNT = 5;
+}
+
+PaymentIdIndex::PaymentIdIndex(bool _enabled) : enabled(_enabled), index(DEFAULT_BUCKET_COUNT, paymentIdHash) {
}
bool PaymentIdIndex::add(const Transaction& transaction) {
diff --git a/src/CryptoNoteCore/BlockchainIndices.h b/src/CryptoNoteCore/BlockchainIndices.h
index cdecc858bb..a64df511dc 100755
--- a/src/CryptoNoteCore/BlockchainIndices.h
+++ b/src/CryptoNoteCore/BlockchainIndices.h
@@ -17,9 +17,10 @@
#pragma once
+#include
+#include