MWEB: Add libmw

This commit is contained in:
David Burkett 2022-01-29 12:04:12 -05:00 committed by Loshan T
parent e735822f3d
commit db90d67828
196 changed files with 21474 additions and 19 deletions

View File

@ -78,7 +78,7 @@ dnl Require C++11 or C++17 compiler (no GNU extensions)
if test "x$use_cxx17" = xyes -o "x$enable_fuzz" = xyes ; then
AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
else
AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory])
AX_CXX_COMPILE_STDCXX([14], [noext], [mandatory])
fi
dnl Check if -latomic is required for <std::atomic>
@ -1723,6 +1723,7 @@ AC_SUBST(EVENT_LIBS)
AC_SUBST(EVENT_PTHREADS_LIBS)
AC_SUBST(ZMQ_LIBS)
AC_SUBST(QR_LIBS)
AC_SUBST(MWEB_LIBS)
AC_SUBST(HAVE_GMTIME_R)
AC_SUBST(HAVE_FDATASYNC)
AC_SUBST(HAVE_FULLFSYNC)

View File

@ -36,6 +36,7 @@ LIBBITCOIN_UTIL=libbitcoin_util.a
LIBBITCOIN_CRYPTO_BASE=crypto/libbitcoin_crypto_base.a
LIBBITCOINQT=qt/libbitcoinqt.a
LIBSECP256K1=secp256k1-zkp/libsecp256k1.la
LIBMW=libmw.a
if ENABLE_ZMQ
LIBBITCOIN_ZMQ=libbitcoin_zmq.a
@ -76,7 +77,8 @@ EXTRA_LIBRARIES += \
$(LIBBITCOIN_CLI) \
$(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_WALLET_TOOL) \
$(LIBBITCOIN_ZMQ)
$(LIBBITCOIN_ZMQ) \
$(LIBMW)
lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS)
@ -105,6 +107,47 @@ if BUILD_BITCOIN_WALLET
endif
endif
LIBMW_CPPFLAGS = -I$(srcdir) -I$(srcdir)/libmw/include -I$(srcdir)/libmw/deps/crypto/include
libmw_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(LIBMW_CPPFLAGS)
libmw_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libmw_a_SOURCES = \
libmw/src/common/Logger.cpp \
libmw/src/crypto/Bulletproofs.cpp \
libmw/src/crypto/ConversionUtil.cpp \
libmw/src/crypto/MuSig.cpp \
libmw/src/crypto/Pedersen.cpp \
libmw/src/crypto/PublicKeys.cpp \
libmw/src/crypto/Schnorr.cpp \
libmw/src/crypto/SecretKeys.cpp \
libmw/src/db/CoinDB.cpp \
libmw/src/db/LeafDB.cpp \
libmw/src/db/MMRInfoDB.cpp \
libmw/src/file/File.cpp \
libmw/src/mmr/ILeafSet.cpp \
libmw/src/mmr/IMMR.cpp \
libmw/src/mmr/Index.cpp \
libmw/src/mmr/LeafSet.cpp \
libmw/src/mmr/LeafSetCache.cpp \
libmw/src/mmr/MemMMR.cpp \
libmw/src/mmr/MMRUtil.cpp \
libmw/src/mmr/PMMRCache.cpp \
libmw/src/mmr/PMMR.cpp \
libmw/src/mmr/PruneList.cpp \
libmw/src/models/block/Block.cpp \
libmw/src/models/crypto/Commitment.cpp \
libmw/src/models/crypto/PublicKey.cpp \
libmw/src/models/tx/Input.cpp \
libmw/src/models/tx/Kernel.cpp \
libmw/src/models/tx/Output.cpp \
libmw/src/models/tx/Transaction.cpp \
libmw/src/models/tx/TxBody.cpp \
libmw/src/node/BlockValidator.cpp \
libmw/src/node/BlockBuilder.cpp \
libmw/src/node/CoinsViewCache.cpp \
libmw/src/node/CoinsViewDB.cpp \
libmw/src/wallet/Keychain.cpp \
libmw/src/wallet/TxBuilder.cpp
.PHONY: FORCE check-symbols check-security
# bitcoin core #
BITCOIN_CORE_H = \
@ -284,7 +327,7 @@ libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
# Contains code accessing mempool and chain state that is meant to be separated
# from wallet and gui code (see node/README.md). Shared code should go in
# libbitcoin_common or libbitcoin_util libraries, instead.
libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) $(LIBMW_CPPFLAGS)
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrdb.cpp \
@ -392,7 +435,7 @@ libbitcoin_wallet_tool_a_SOURCES = \
$(BITCOIN_CORE_H)
# crypto primitives library
crypto_libbitcoin_crypto_base_a_CPPFLAGS = $(AM_CPPFLAGS) $(SSL_CFLAGS)
crypto_libbitcoin_crypto_base_a_CPPFLAGS = $(AM_CPPFLAGS) $(SSL_CFLAGS) $(BITCOIN_INCLUDES)
crypto_libbitcoin_crypto_base_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/aes.cpp \
@ -424,7 +467,8 @@ crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/sha512.cpp \
crypto/sha512.h \
crypto/siphash.cpp \
crypto/siphash.h
crypto/siphash.h \
libmw/src/crypto/Hasher.cpp
if USE_ASM
crypto_libbitcoin_crypto_base_a_SOURCES += crypto/sha256_sse4.cpp
@ -599,9 +643,10 @@ litecoin_bin_ldadd = \
$(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) \
$(LIBMEMENV) \
$(LIBSECP256K1)
$(LIBSECP256K1) \
$(LIBMW)
litecoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS)
litecoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS) $(MWEB_LIBS)
litecoind_SOURCES = $(litecoin_daemon_sources)
litecoind_CPPFLAGS = $(litecoin_bin_cppflags)
@ -631,7 +676,7 @@ litecoin_cli_LDADD = \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO)
litecoin_cli_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS)
litecoin_cli_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) $(MWEB_LIBS)
#
# bitcoin-tx binary #
@ -650,10 +695,10 @@ litecoin_tx_LDADD = \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
$(LIBSECP256K1)
$(LIBSECP256K1) \
$(LIBMW)
litecoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
#
litecoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(MWEB_LIBS)
# bitcoin-wallet binary #
litecoin_wallet_SOURCES = bitcoin-wallet.cpp
@ -677,8 +722,8 @@ if GLIBC_BACK_COMPAT
endif
libbitcoinconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS)
libbitcoinconsensus_la_LIBADD = $(LIBSECP256K1) $(CRYPTO_LIBS)
libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL $(SSL_CFLAGS)
libbitcoinconsensus_la_LIBADD = $(LIBSECP256K1) $(CRYPTO_LIBS) $(MWEB_LIBS)
libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1-zkp/include -DBUILD_BITCOIN_INTERNAL $(SSL_CFLAGS) $(LIBMW_CPPFLAGS)
libbitcoinconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
endif

View File

@ -63,7 +63,8 @@ bench_bench_litecoin_LDADD = \
$(LIBSECP256K1) \
$(LIBUNIVALUE) \
$(EVENT_PTHREADS_LIBS) \
$(EVENT_LIBS)
$(EVENT_LIBS) \
$(LIBMW)
if ENABLE_ZMQ
bench_bench_litecoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
@ -74,7 +75,7 @@ bench_bench_litecoin_SOURCES += bench/coin_selection.cpp
bench_bench_litecoin_SOURCES += bench/wallet_balance.cpp
endif
bench_bench_litecoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(SQLITE_LIBS)
bench_bench_litecoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(SQLITE_LIBS) $(MWEB_LIBS)
bench_bench_litecoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)

View File

@ -323,7 +323,7 @@ if ENABLE_ZMQ
litecoin_qt_ldadd += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
litecoin_qt_ldadd += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \
$(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) $(CRYPTO_LIBS) \
$(LIBMW) $(MWEB_LIBS) $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) $(CRYPTO_LIBS) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(SQLITE_LIBS)
litecoin_qt_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
litecoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX

View File

@ -54,7 +54,7 @@ if ENABLE_ZMQ
qt_test_test_litecoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
qt_test_test_litecoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
$(LIBLEVELDB_SSE42) $(LIBMEMENV) $(LIBMW) $(MWEB_LIBS) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
$(QR_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(SQLITE_LIBS)
qt_test_test_litecoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)

View File

@ -202,6 +202,7 @@ FUZZ_SUITE_LD_COMMON = \
$(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) \
$(BOOST_LIBS) \
$(MWEB_LIBS) \
$(LIBMEMENV) \
$(LIBSECP256K1) \
$(EVENT_LIBS) \
@ -310,15 +311,53 @@ BITCOIN_TEST_SUITE += \
wallet/test/init_test_fixture.h
endif
BITCOIN_TEST_SUITE += \
libmw/test/framework/src/TxBuilder.cpp \
libmw/test/framework/src/models/Tx.cpp
BITCOIN_TESTS += \
libmw/test/tests/consensus/Test_Aggregation.cpp \
libmw/test/tests/consensus/Test_KernelSumValidator.cpp \
libmw/test/tests/consensus/Test_StealthSumValidator.cpp \
libmw/test/tests/consensus/Test_Weight.cpp \
libmw/test/tests/crypto/Test_AddCommitments.cpp \
libmw/test/tests/crypto/Test_AggSig.cpp \
libmw/test/tests/crypto/Test_Keys.cpp \
libmw/test/tests/crypto/Test_RangeProofs.cpp \
libmw/test/tests/db/Test_LeafDB.cpp \
libmw/test/tests/mmr/Test_Index.cpp \
libmw/test/tests/mmr/Test_LeafIndex.cpp \
libmw/test/tests/mmr/Test_LeafSetCache.cpp \
libmw/test/tests/mmr/Test_LeafSet.cpp \
libmw/test/tests/mmr/Test_MMR.cpp \
libmw/test/tests/mmr/Test_MMRUtil.cpp \
libmw/test/tests/mmr/Test_PruneList.cpp \
libmw/test/tests/models/block/Test_Block.cpp \
libmw/test/tests/models/block/Test_Header.cpp \
libmw/test/tests/models/crypto/Test_BigInteger.cpp \
libmw/test/tests/models/tx/Test_Input.cpp \
libmw/test/tests/models/tx/Test_Kernel.cpp \
libmw/test/tests/models/tx/Test_Output.cpp \
libmw/test/tests/models/tx/Test_PegInCoin.cpp \
libmw/test/tests/models/tx/Test_PegOutCoin.cpp \
libmw/test/tests/models/tx/Test_Transaction.cpp \
libmw/test/tests/models/tx/Test_TxBody.cpp \
libmw/test/tests/models/tx/Test_UTXO.cpp \
libmw/test/tests/node/Test_BlockBuilder.cpp \
libmw/test/tests/node/Test_BlockValidator.cpp \
libmw/test/tests/node/Test_CoinsView.cpp \
libmw/test/tests/node/Test_MineChain.cpp \
libmw/test/tests/node/Test_Reorg.cpp
test_test_litecoin_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
test_test_litecoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EVENT_CFLAGS)
test_test_litecoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EVENT_CFLAGS) $(LIBMW_CPPFLAGS) -Ilibmw/test/framework/include
test_test_litecoin_LDADD = $(LIBTEST_UTIL)
if ENABLE_WALLET
test_test_litecoin_LDADD += $(LIBBITCOIN_WALLET)
endif
test_test_litecoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(LIBMW) $(MWEB_LIBS) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_litecoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_test_litecoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(CRYPTO_LIBS) $(SQLITE_LIBS)

9
src/libmw/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
out/
build/
bin/
build-aux/
.vs/
CMakeSettings.json
libmw-config.h
stamp-h1

21
src/libmw/LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018-2019 David Burkett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

45
src/libmw/README.md Normal file
View File

@ -0,0 +1,45 @@
# libmw
Modular library for supporting Litecoin's implementation of the MWEB (Mimblewimble Extension Block).
[Build instructions](doc/build.md)
## Design
## deps/
Contains third-party dependencies. Most of our dependencies come from vcpkg,
but here we vendor a few libraries since a suitable alternative was not found on vcpkg.
### caches/
Header-only implementations of common caching algorithms.
Repo: https://github.com/vpetrigo/caches/
### crypto/
Crypto libraries not provided by litecoin or secp256k1-zkp.
Currently, this just contains The reference implementation of blake2b.
### ghc/
A header-only implementation of the C\+\+17 std::filesystem standard, but made compatible with C\+\+11 and C\+\+14.
While boost\:\:filesystem is already pulled in by litecoin, I was uncomfortable with its API and its handling of unicode conversions.
Someone more familiar with boost::filesystem should be able to eliminate the need for this dependency.
### secp256k1-zkp/
The elliptic curve cryptography, including schnorr, musig, pedersen, and bulletproofs modules.
This code comes from https://github.com/mimblewimble/secp256k1-zkp,
which was built on top of https://github.com/elementsproject/secp256k1-zkp,
which was built on top of https://github.com/bitcoin/bitcoin/tree/master/src/secp256k1
Before releasing, we should see if the latest version of https://github.com/elementsproject/secp256k1-zkp contains all of the modules we need,
since it gets a lot of attention from cryptographers. Additionally, it would be wise to remove this from the deps directory,
and just replace the more-limited secp256k1 dependency that's already included in Litecoin.
Otherwise, we'll have 2 versions to maintain, and will very likely have namespace clashes to deal with.
## include/
## src/

View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2015-2017, Vladimir Petrigo
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,68 @@
[![Build Status](https://travis-ci.org/vpetrigo/caches.svg?branch=master)](https://travis-ci.org/vpetrigo/caches)
[![Build status](https://ci.appveyor.com/api/projects/status/5tcwwry337fbjgcb/branch/master?svg=true)](https://ci.appveyor.com/project/vpetrigo/caches/branch/master)
# C++ Cache implementation
This project implements a simple thread-safe cache with several page replacement policies:
* Least Recently Used
* First-In/First-Out
* Least Frequently Used
More about cache algorithms and policy you could read on [Wikipedia](https://en.wikipedia.org/wiki/Cache_algorithms)
# Usage
Using this library is simple. It is necessary to include header with the cache implementation (`cache.hpp` file)
and appropriate header with the cache policy if it is needed. If not then the non-special algorithm will be used (it removes
the last element which key is the last in the internal container).
Currently there is only three of them:
* `fifo_cache_policy.hpp`
* `lfu_cache_policy.hpp`
* `lru_cache_policy.hpp`
Example for the LRU policy:
```cpp
#include <string>
#include "cache.hpp"
#include "lru_cache_policy.hpp"
// alias for easy class typing
template <typename Key, typename Value>
using lru_cache_t = typename caches::fixed_sized_cache<Key, Value, LRUCachePolicy<Key>>;
void foo(...) {
constexpr std::size_t CACHE_SIZE = 256;
lru_cache_t<std::string, int> cache(CACHE_SIZE);
cache.Put("Hello", 1);
cache.Put("world", 2);
std::cout << cache.Get("Hello") << cache.Get("world") << std::endl;
// "12"
}
```
# Requirements
The only requirement is a compatible C++11 compiler.
This project was tested in the environments listed below:
* MinGW64 ([MSYS2 project](https://msys2.github.io/))
* Clang 3.8.0
* GCC 5.3.0
* MSVC (VS 2015)
* FreeBSD
* Clang 3.4.1
If you have any issues with the library building, let me know please.
# Contributing
Please fork this repository and contribute back using [pull requests](https://github.com/vpetrigo/caches/pulls).
Features can be requested using [issues](https://github.com/vpetrigo/caches/issues). All code, comments, and
critiques are greatly appreciated.

View File

@ -0,0 +1,11 @@
#pragma once
#include "internal/cache.hpp"
#include "internal/fifo_cache_policy.hpp"
#include "internal/lru_cache_policy.hpp"
template <typename Key, typename Value>
using FIFOCache = typename caches::fixed_sized_cache<Key, Value, caches::FIFOCachePolicy<Key>>;
template <typename Key, typename Value>
using LRUCache = typename caches::fixed_sized_cache<Key, Value, caches::LRUCachePolicy<Key>>;

View File

@ -0,0 +1,153 @@
#ifndef CACHE_HPP
#define CACHE_HPP
#include "cache_policy.hpp"
#include <cstddef>
#include <functional>
#include <limits>
#include <memory>
#include <mutex>
#include <unordered_map>
namespace caches
{
// Base class for caching algorithms
template <typename Key, typename Value, typename Policy = NoCachePolicy<Key>>
class fixed_sized_cache
{
public:
using iterator = typename std::unordered_map<Key, Value>::iterator;
using const_iterator =
typename std::unordered_map<Key, Value>::const_iterator;
using operation_guard = typename std::lock_guard<std::mutex>;
using Callback =
typename std::function<void(const Key &key, const Value &value)>;
fixed_sized_cache(size_t max_size, const Policy &policy = Policy(),
Callback OnErase = [](const Key &, const Value &) {})
: cache_policy(policy), max_cache_size(max_size),
OnEraseCallback(OnErase)
{
if (max_cache_size == 0)
{
max_cache_size = std::numeric_limits<size_t>::max();
}
}
~fixed_sized_cache()
{
Clear();
}
void Put(const Key &key, const Value &value)
{
operation_guard lock{safe_op};
auto elem_it = FindElem(key);
if (elem_it == cache_items_map.end())
{
// add new element to the cache
if (cache_items_map.size() + 1 > max_cache_size)
{
auto disp_candidate_key = cache_policy.ReplCandidate();
Erase(disp_candidate_key);
}
Insert(key, value);
}
else
{
// update previous value
Update(key, value);
}
}
const Value &Get(const Key &key) const
{
operation_guard lock{safe_op};
auto elem_it = FindElem(key);
if (elem_it == cache_items_map.end())
{
throw std::range_error{"No such element in the cache"};
}
cache_policy.Touch(key);
return elem_it->second;
}
bool Cached(const Key &key) const
{
operation_guard lock{safe_op};
return FindElem(key) != cache_items_map.end();
}
size_t Size() const
{
operation_guard lock{safe_op};
return cache_items_map.size();
}
void Clear()
{
operation_guard lock{safe_op};
for (auto it = begin(); it != end(); ++it)
{
cache_policy.Erase(it->first);
OnEraseCallback(it->first, it->second);
}
cache_items_map.clear();
}
typename std::unordered_map<Key, Value>::const_iterator begin() const
{
return cache_items_map.begin();
}
typename std::unordered_map<Key, Value>::const_iterator end() const
{
return cache_items_map.end();
}
protected:
void Insert(const Key &key, const Value &value)
{
cache_policy.Insert(key);
cache_items_map.emplace(std::make_pair(key, value));
}
void Erase(const Key &key)
{
cache_policy.Erase(key);
auto elem_it = FindElem(key);
OnEraseCallback(key, elem_it->second);
cache_items_map.erase(elem_it);
}
void Update(const Key &key, const Value &value)
{
cache_policy.Touch(key);
cache_items_map[key] = value;
}
const_iterator FindElem(const Key &key) const
{
return cache_items_map.find(key);
}
private:
std::unordered_map<Key, Value> cache_items_map;
mutable Policy cache_policy;
mutable std::mutex safe_op;
size_t max_cache_size;
Callback OnEraseCallback;
};
}
#endif // CACHE_HPP

View File

@ -0,0 +1,58 @@
#ifndef CACHE_POLICY_HPP
#define CACHE_POLICY_HPP
#include <unordered_set>
namespace caches
{
template <typename Key> class ICachePolicy
{
public:
virtual ~ICachePolicy()
{
}
// handle element insertion in a cache
virtual void Insert(const Key &key) = 0;
// handle request to the key-element in a cache
virtual void Touch(const Key &key) = 0;
// handle element deletion from a cache
virtual void Erase(const Key &key) = 0;
// return a key of a replacement candidate
virtual const Key &ReplCandidate() const = 0;
};
template <typename Key> class NoCachePolicy : public ICachePolicy<Key>
{
public:
NoCachePolicy() = default;
~NoCachePolicy() override = default;
void Insert(const Key &key) override
{
key_storage.emplace(key);
}
void Touch(const Key &key) override
{
// do not do anything
}
void Erase(const Key &key) override
{
key_storage.erase(key);
}
// return a key of a displacement candidate
const Key &ReplCandidate() const override
{
return *key_storage.cbegin();
}
private:
std::unordered_set<Key> key_storage;
};
} // namespace caches
#endif // CACHE_POLICY_HPP

View File

@ -0,0 +1,41 @@
#ifndef FIFO_CACHE_POLICY_HPP
#define FIFO_CACHE_POLICY_HPP
#include "cache_policy.hpp"
#include <list>
namespace caches
{
template <typename Key> class FIFOCachePolicy : public ICachePolicy<Key>
{
public:
FIFOCachePolicy() = default;
~FIFOCachePolicy() = default;
void Insert(const Key &key) override
{
fifo_queue.emplace_front(key);
}
// handle request to the key-element in a cache
void Touch(const Key &key) override
{
// nothing to do here in the FIFO strategy
}
// handle element deletion from a cache
void Erase(const Key &key) override
{
fifo_queue.pop_back();
}
// return a key of a replacement candidate
const Key &ReplCandidate() const override
{
return fifo_queue.back();
}
private:
std::list<Key> fifo_queue;
};
} // namespace caches
#endif // FIFO_CACHE_POLICY_HPP

View File

@ -0,0 +1,59 @@
#ifndef LFU_CACHE_POLICY_HPP
#define LFU_CACHE_POLICY_HPP
#include "cache_policy.hpp"
#include <cstddef>
#include <iostream>
#include <map>
#include <unordered_map>
namespace caches
{
template <typename Key> class LFUCachePolicy : public ICachePolicy<Key>
{
public:
using lfu_iterator = typename std::multimap<std::size_t, Key>::iterator;
LFUCachePolicy() = default;
~LFUCachePolicy() override = default;
void Insert(const Key &key) override
{
constexpr std::size_t INIT_VAL = 1;
// all new value initialized with the frequency 1
lfu_storage[key] = frequency_storage.emplace_hint(
frequency_storage.cbegin(), INIT_VAL, key);
}
void Touch(const Key &key) override
{
// get the previous frequency value of a key
auto elem_for_update = lfu_storage[key];
auto updated_elem =
std::make_pair(elem_for_update->first + 1, elem_for_update->second);
// update the previous value
frequency_storage.erase(elem_for_update);
lfu_storage[key] = frequency_storage.emplace_hint(
frequency_storage.cend(), std::move(updated_elem));
}
void Erase(const Key &key) override
{
frequency_storage.erase(lfu_storage[key]);
lfu_storage.erase(key);
}
const Key &ReplCandidate() const override
{
// at the beginning of the frequency_storage we have the
// least frequency used value
return frequency_storage.cbegin()->second;
}
private:
std::multimap<std::size_t, Key> frequency_storage;
std::unordered_map<Key, lfu_iterator> lfu_storage;
};
} // namespace caches
#endif // LFU_CACHE_POLICY_HPP

View File

@ -0,0 +1,49 @@
#ifndef LRU_CACHE_POLICY_HPP
#define LRU_CACHE_POLICY_HPP
#include "cache_policy.hpp"
#include <list>
#include <unordered_map>
namespace caches
{
template <typename Key> class LRUCachePolicy : public ICachePolicy<Key>
{
public:
using lru_iterator = typename std::list<Key>::iterator;
LRUCachePolicy() = default;
~LRUCachePolicy() = default;
void Insert(const Key &key) override
{
lru_queue.emplace_front(key);
key_finder[key] = lru_queue.begin();
}
void Touch(const Key &key) override
{
// move the touched element at the beginning of the lru_queue
lru_queue.splice(lru_queue.begin(), lru_queue, key_finder[key]);
}
void Erase(const Key &) override
{
// remove the least recently used element
key_finder.erase(lru_queue.back());
lru_queue.pop_back();
}
// return a key of a displacement candidate
const Key &ReplCandidate() const override
{
return lru_queue.back();
}
private:
std::list<Key> lru_queue;
std::unordered_map<Key, lru_iterator> key_finder;
};
} // namespace caches
#endif // LRU_CACHE_POLICY_HPP

View File

@ -0,0 +1,19 @@
Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,937 @@
![Supported Platforms](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows%20%7C%20FreeBSD-blue.svg)
![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)
[![Build Status](https://travis-ci.org/gulrak/filesystem.svg?branch=master)](https://travis-ci.org/gulrak/filesystem)
[![Build Status](https://ci.appveyor.com/api/projects/status/t07wp3k2cddo0hpo/branch/master?svg=true)](https://ci.appveyor.com/project/gulrak/filesystem)
[![Build Status](https://api.cirrus-ci.com/github/gulrak/filesystem.svg?branch=master)](https://cirrus-ci.com/github/gulrak/filesystem)
[![Build Status](https://cloud.drone.io/api/badges/gulrak/filesystem/status.svg?ref=refs/heads/master)](https://cloud.drone.io/gulrak/filesystem)
[![Coverage Status](https://coveralls.io/repos/github/gulrak/filesystem/badge.svg?branch=master)](https://coveralls.io/github/gulrak/filesystem?branch=master)
[![Latest Release Tag](https://img.shields.io/github/tag/gulrak/filesystem.svg)](https://github.com/gulrak/filesystem/tree/v1.5.2)
# Filesystem
This is a header-only single-file std::filesystem compatible helper library,
based on the C++17 and C++20 specs, but implemented for C++11, C++14, C++17 or C++20
(tightly following the C++17 standard with very few documented exceptions). It is currently tested on
macOS 10.12/10.14/10.15, Windows 10, Ubuntu 18.04, CentOS 7, CentOS 8, FreeBSD 12
and Alpine ARM/ARM64 Linux but should work on other systems too, as long as you have
at least a C++11 compatible compiler. It should work with Android NDK, Emscripten and I even
had reports of it being used on iOS (within sandboxing constraints).
It is of course in its own namespace `ghc::filesystem` to not interfere with a regular `std::filesystem` should you use it in a mixed C++17
environment (which is possible).
*Test coverage is well above 90%, and starting with v1.3.6 and in v1.5.0
more time was invested in benchmarking and optimizing parts of the library. I'll try
to continue to optimize some parts and refactor others, striving
to improve it as long as it doesn't introduce additional C++17/C++20 compatibility
issues. Feedback is always welcome. Simply open an issue if you see something missing
or wrong or not behaving as expected and I'll comment.*
## Motivation
I'm often in need of filesystem functionality, mostly `fs::path`, but directory
access too, and when beginning to use C++11, I used that language update
to try to reduce my third-party dependencies. I could drop most of what
I used, but still missed some stuff that I started implementing for the
fun of it. Originally I based these helpers on my own coding- and naming
conventions. When C++17 was finalized, I wanted to use that interface,
but it took a while, to push myself to convert my classes.
The implementation is closely based on chapter 30.10 from the C++17 standard
and a draft close to that version is
[Working Draft N4687](https://github.com/cplusplus/draft/raw/master/papers/n4687.pdf).
It is from after the standardization of C++17 but it contains the latest filesystem
interface changes compared to the
[Working Draft N4659](https://github.com/cplusplus/draft/raw/master/papers/n4659.pdf).
Staring with v1.4.0, when compiled using C++20, it adapts to the changes according to path sorting order
and `std::u8string` handling from [Working Draft N4860](https://isocpp.org/files/papers/N4860.pdf).
I want to thank the people working on improving C++, I really liked how the language
evolved with C++11 and the following standards. Keep on the good work!
## Why the namespace GHC?
If you ask yourself, what `ghc` is standing for, it is simply
`gulraks helper classes`, yeah, I know, not very imaginative, but I wanted a
short namespace and I use it in some of my private classes (so **it has nothing
to do with Haskell**, sorry for the name clash).
## Platforms
`ghc::filesystem` is developed on macOS but CI tested on macOS, Windows,
various Linux Distributions and FreeBSD. It should work on any of these with a C++11-capable
compiler. Also there are some checks to hopefully better work on Android, but
as I currently don't test with the Android NDK, I wouldn't call it a
supported platform yet, same is valid for using it with Emscripten. It is now
part of the detected platforms, I fixed the obvious issues and ran some tests with
it, so it should be fine. All in all, I don't see it replacing `std::filesystem`
where full C++17 or C++20 is available, it doesn't try to be a "better"
`std::filesystem`, just an almost drop-in if you can't use it (with the exception
of the UTF-8 preference).
:information_source: **Important:** _This implementation is following the ["UTF-8 Everywhere" philosophy](https://utf8everywhere.org/) in that all
`std::string` instances will be interpreted the same as `std::u8string` encoding
wise and as being in UTF-8. The `std::u16string` will be seen as UTF-16. See *Differences in API*
for more information._
Unit tests are currently run with:
* macOS 10.12: Xcode 9.2 (clang-900.0.39.2), GCC 9.2, Clang 9.0, macOS 10.13: Xcode 10.1, macOS 10.14: Xcode 11.2, macOS 10.15: Xcode 11.6
* Windows: Visual Studio 2017, Visual Studio 2015, Visual Studio 2019, MinGW GCC 6.3 (Win32), GCC 7.2 (Win64)
* Linux (Ubuntu): GCC (5.5, 6.5, 7.4, 8.3, 9.2), Clang (5.0, 6.0, 7.1, 8.0, 9.0)
* Linux (Alpine ARM/ARM64): GCC 9.2.0
* FreeBSD: Clang 8.0
## Tests
The header comes with a set of unit-tests and uses [CMake](https://cmake.org/)
as a build tool and [Catch2](https://github.com/catchorg/Catch2) as test framework.
All tests agains this implementation should succeed, depending on your environment
it might be that there are some warnings, e.g. if you have no rights to create
Symlinks on Windows or at least the test thinks so, but these are just informative.
To build the tests from inside the project directory under macOS or Linux just:
```cpp
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Debug ..
make
```
This generates `filesystem_test`, the binary that runs all tests.
If the default compiler is a GCC 8 or newer, or Clang 7 or newer, it
additionally tries to build a version of the test binary compiled against GCCs/Clangs
`std::filesystem` implementation, named `std_filesystem_test`
as an additional test of conformance. Ideally all tests should compile and
succeed with all filesystem implementations, but in reality, there are
some differences in behavior, sometimes due to room for interpretation in
in the standard, and there might be issues in these implementations too.
## Usage
### Downloads
The latest release version is [v1.5.2](https://github.com/gulrak/filesystem/tree/v1.5.2) and
source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.5.2).
The latest pre-native-backend version is [v1.4.0](https://github.com/gulrak/filesystem/tree/v1.4.0) and
source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.4.0).
The latest pre-C++20-support release version is [v1.3.10](https://github.com/gulrak/filesystem/tree/v1.3.10) and
source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.3.10).
### Using it as Single-File-Header
As `ghc::filesystem` is at first a header-only library, it should be enough to copy the header
or the `include/ghc` directory into your project folder oder point your include path to this place and
simply include the `filesystem.hpp` header (or `ghc/filesystem.hpp` if you use the subdirectory).
Everything is in the namespace `ghc::filesystem`, so one way to use it only as
a fallback could be:
```cpp
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#include <filesystem>
namespace fs = std::filesystem;
#endif
#endif
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem;
#endif
```
**Note that this code uses a two-stage preprocessor condition because Visual Studio 2015
doesn't like the `(<...>)` syntax, even if it could cut evaluation early before. This code also
used the minimum deployment target to detect if std::filesystem really is available on macOS
compilation.**
**Note also, this detection now works on MSVC versions prior to 15.7 on, or without setting
the `/Zc:__cplusplus` compile switch that would fix `__cplusplus` on MSVC. (Without the switch
the compiler allways reports `199711L`
([see](https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/)),
but `_MSVC_LANG` works without it.**
If you want to also use the `fstream` wrapper with `path` support as fallback,
you might use:
```cpp
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#include <filesystem>
namespace fs {
using namespace std::filesystem;
using ifstream = std::ifstream;
using ofstream = std::ofstream;
using fstream = std::fstream;
}
#endif
#endif
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp>
namespace fs {
using namespace ghc::filesystem;
using ifstream = ghc::filesystem::ifstream;
using ofstream = ghc::filesystem::ofstream;
using fstream = ghc::filesystem::fstream;
}
#endif
```
Now you have e.g. `fs::ofstream out(somePath);` and it is either the wrapper or
the C++17 `std::ofstream`.
**Be aware, as a header-only library, it is not hiding the fact, that it
uses system includes, so they "pollute" your global namespace. Use the
forwarding-/implementation-header based approach (see below) to avoid this.**
:information_source: **Hint:** There is an additional header named `ghc/fs_std.hpp` that implements this
dynamic selection of a filesystem implementation, that you can include
instead of `ghc/filesystem.hpp` when you want std::filesystem where
available and ghc::filesystem where not.
### Using it as Forwarding-/Implementation-Header
Alternatively, starting from v1.1.0 `ghc::filesystem` can also be used by
including one of two additional wrapper headers. These allow to include
a forwarded version in most places (`ghc/fs_fwd.hpp`) while hiding the
implementation details in a single cpp that includes `ghc/fs_impl.hpp` to
implement the needed code. That way system includes are only visible from
inside the cpp, all other places are clean.
Be aware, that it is currently not supported to hide the implementation
into a Windows-DLL, as a DLL interface with C++ standard templates in interfaces
is a different beast. If someone is willing to give it a try, I might integrate
a PR but currently working on that myself is not a priority.
If you use the forwarding/implementation approach, you can still use the dynamic
switching like this:
```cpp
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#include <filesystem>
namespace fs {
using namespace std::filesystem;
using ifstream = std::ifstream;
using ofstream = std::ofstream;
using fstream = std::fstream;
}
#endif
#endif
#ifndef GHC_USE_STD_FS
#include <ghc/fs-fwd.hpp>
namespace fs {
using namespace ghc::filesystem;
using ifstream = ghc::filesystem::ifstream;
using ofstream = ghc::filesystem::ofstream;
using fstream = ghc::filesystem::fstream;
}
#endif
```
and in the implementation hiding cpp, you might use (before any include that includes `ghc/fs_fwd.hpp`
to take precedence:
```cpp
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#endif
#endif
#ifndef GHC_USE_STD_FS
#define GHC_FILESYSTEM_IMPLEMENTATION
#include <ghc/filesystem.hpp>
#endif
```
:information_source: **Hint:** There are additional helper headers, named `ghc/fs_std_fwd.hpp` and
`ghc/fs_std_impl.hpp` that use this technique, so you can simply include them
if you want to dynamically select the filesystem implementation. they also
enable the `wchar_t` support on `ghc::filesystem` on Windows, so the resulting
implementation in the `fs` namespace will be compatible.
### Git Submodule and CMake
Starting from v1.1.0, it is possible to add `ghc::filesystem`
as a git submodule, add the directory to your `CMakeLists.txt` with
`add_subdirectory()` and then simply use `target_link_libraries(your-target ghc_filesystem)`
to ensure correct include path that allow `#include <ghc/filesystem.hpp>`
to work.
The `CMakeLists.txt` offers a few options to customize its behaviour:
* `GHC_FILESYSTEM_BUILD_TESTING` - Compile tests, default is `OFF` when used as
a submodule, else `ON`.
* `GHC_FILESYSTEM_BUILD_EXAMPLES` - Compile the examples, default is `OFF` when used as
a submodule, else `ON`.
* `GHC_FILESYSTEM_WITH_INSTALL` - Add install target to build, default is `OFF` when used as
a submodule, else `ON`.
### Versioning
There is a version macro `GHC_FILESYSTEM_VERSION` defined in case future changes
might make it needed to react on the version, but I don't plan to break anything.
It's the version as decimal number `(major * 10000 + minor * 100 + patch)`.
:information_source: **Note:** Only even patch versions will be used for releases
and odd patch version will only be used for in between commits while working on
the next version.
## Documentation
There is almost no documentation in this release, as any `std::filesystem`
documentation would work, besides the few differences explained in the next
section. So you might head over to https://en.cppreference.com/w/cpp/filesystem
for a description of the components of this library.
When compiling with C++11, C++14 or C++17, the API is following the C++17
standard, where possible, with the exception that `std::string_view` parameters
are only supported on C++17. When Compiling with C++20, `ghc::filesysytem`
defaults to the C++20 API, with the `char8_t` and `std::u8string` interfaces
and the deprecated `fs::u8path` factory method.
:information_source: **Note:** If the C++17 API should be enforced even in C++20 mode,
use the define `GHC_FILESYSTEM_ENFORCE_CPP17_API`.
Even then it is possible to create `fws::path` from `std::u8string` but
`fs::path::u8string()` and `fs::path::generic_u8string()` return normal
UTF-8 encoded `std::string` instances, so code written for C++17 could
still work with `ghc::filesystem` when compiled with C++20.
The only additions to the standard are documented here:
### `ghc::filesystem::ifstream`, `ghc::filesystem::ofstream`, `ghc::filesystem::fstream`
These are simple wrappers around `std::ifstream`, `std::ofstream` and `std::fstream`.
They simply add an `open()` method and a constuctor with an `ghc::filesystem::path`
argument as the `fstream` variants in C++17 have them.
### `ghc::filesystem::u8arguments`
This is a helper class that currently checks for UTF-8 encoding on non-Windows platforms but on Windows it
fetches the command line arguments als Unicode strings from the OS with
```cpp
::CommandLineToArgvW(::GetCommandLineW(), &argc)
```
and then converts them to UTF-8, and replaces `argc` and `argv`. It is a guard-like
class that reverts its changes when going out of scope.
So basic usage is:
```cpp
namespace fs = ghc::filesystem;
int main(int argc, char* argv[])
{
fs::u8arguments u8guard(argc, argv);
if(!u8guard.valid()) {
std::cerr << "Bad encoding, needs UTF-8." << std::endl;
exit(EXIT_FAILURE);
}
// now use argc/argv as usual, they have utf-8 enconding on windows
// ...
return 0;
}
```
That way `argv` is UTF-8 encoded as long as the scope from `main` is valid.
**Note:** On macOS, while debugging under Xcode the code currently will return
`false` as Xcode starts the application with `US-ASCII` as encoding, no matter what
encoding is actually used and even setting `LC_ALL` in the product scheme doesn't
change anything. I still need to investigate this.
## Differences
As this implementation is based on existing code from my private helper
classes, it derived some constraints of it. Starting from v1.5.0 most of the
differences between this and the standard C++17/C++20 API where removed.
### LWG Defects
This implementation has switchable behavior for the LWG defects
[#2682](https://wg21.cmeerw.net/lwg/issue2682),
[#2935](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2935),
[#2936](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2936) and
[#2937](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2937).
The currently selected behavior (starting from v1.4.0) is following
[#2682](https://wg21.cmeerw.net/lwg/issue2682), [#2936](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2936),
[#2937](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2937) but
not following [#2935](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2935),
as I feel it is a bug to report no error on a `create_directory()` or `create_directories()`
where a regular file of the same name prohibits the creation of a directory and forces
the user of those functions to double-check via `fs::is_directory` if it really worked.
The more intuitive approach to directory creation of treating a file with that name as an
error is also advocated by the newer paper
[WG21 P1164R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1164r0.pdf), the revison
P1161R1 was agreed upon on Kona 2019 meeting [see merge](https://github.com/cplusplus/draft/issues/2703)
and GCC by now switched to following its proposal
([GCC #86910](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86910)).
### Not Implemented on C++ before C++17
```cpp
// methods in ghc::filesystem::path:
path& operator+=(basic_string_view<value_type> x);
int compare(basic_string_view<value_type> s) const;
```
These are not implemented under C++11 and C++14, as there is no
`std::basic_string_view` available and I did want to keep this
implementation self-contained and not write a full C++17-upgrade for
C++11/14. Starting with v1.1.0 these are supported when compiling
ghc::filesystem under C++17 of C++20.
Starting with v1.5.2 `ghc::filesystem` will try to allow the use of
`std::experimental::basic_string_view` where it detects is availability.
Additionally if you have a `basic_string_view` compatible c++11
implementation it can be used instead of `std::basic_string_view`
by defining `GHC_HAS_CUSTOM_STRING_VIEW` and importing the
implementation into the `ghc::filesystem` namespace with:
```cpp
namespace ghc {
namespace filesystem {
using my::basic_string_view;
}
}
```
before including the filesystem header.
### Differences in API
To not depend on any external third party libraries and still stay portable and
compact, this implementation is following the ["UTF-8 Everywhere" philosophy](https://utf8everywhere.org/) in that all
`std::string` instances will be interpreted the same as `std::u8string` encoding
wise and as being in UTF-8. The `std::u16string` will be seen as UTF-16 and `std::u32string` will be
seen as unicode codepoints. Depending on the size of `std::wstring` characters, it will handle
`std::wstring` as being UTF-16 (e.g. Windows) or `char32_t` unicode codepoints
(currently all other platforms).
#### Differences of Specific Interfaces
Starting with v1.5.0 `ghc::filesystem` is following the C++17 standard in
using `wchar_t` and `std::wstring` on Windows as the types internally used
for path representation. It is still possible to get the old behavior by defining
`GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE` and get `filesystem::path::string_type` as
`std::string` and `filesystem::path::value_type` as `wchar_t`.
If you need to call some Windows API, with v1.5.0 and above, simply
use the W-variant of the Windows-API call (e.g. `GetFileAttributesW(p.c_str())`).
:information_source: **Note:** _When using the old behavior by defining
`GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE`, use the `path::wstring()` member
(e.g. `GetFileAttributesW(p.wstring().c_str())`). This gives you the
Unicode variant independant of the `UNICODE` macro and makes sharing code
between Windows, Linux and macOS easier and works with `std::filesystem` and
`ghc::filesystem`._
```cpp
std::string path::u8string() const;
std::string path::generic_u8string() const;
vs.
std::u8string path::u8string() const;
std::u8string path::generic_u8string() const;
```
The return type of these two methods is depending on the used C++ standard
and if `GHC_FILESYSTEM_ENFORCE_CPP17_API` is defined. On C++11, C++14 and
C++17 or when `GHC_FILESYSTEM_ENFORCE_CPP17_API` is defined, the return
type is `std::string`, and on C++20 without the define it is `std::u8string`.
### Differences in Behavior
I created a wiki entry about quite a lot of [behavioral differences](https://github.com/gulrak/filesystem/wiki/Differences-to-Standard-Filesystem-Implementations)
between different `std::filesystem` implementations that could result in a
mention here, but this readme only tries to address the design choice
differences between `ghc::filesystem` and those. I try to update the wiki page
from time to time.
Any additional observations are welcome!
#### fs.path ([ref](https://en.cppreference.com/w/cpp/filesystem/path))
Since v1.5.0 the complete inner mechanics of this implementations `fs::path`
where changed to the _native_ format as the internal representation.
Creating any mixed slash `fs::path` object under Windows (e.g. with `"C:\foo/bar"`)
will lead clean path with `"C:\foo\bar"` via `native()` and `"C:/foo/bar"` via
`generic_string()` API. On all platforms redundant additional separators are
removed, even if this is not enforced by the standard and other implementations
mostly not do this.
Additionally this implementation follows the standards suggestion to handle
posix paths of the form `"//host/path"` and USC path on windows also as having
a root-name (e.g. `"//host"`). The GCC implementation didn't choose to do that
while testing on Ubuntu 18.04 and macOS with GCC 8.1.0 or Clang 7.0.0. This difference
will show as warnings under std::filesystem. This leads to a change in the
algorithm described in the standard for `operator/=(path& p)` where any path
`p` with `p.is_absolute()` will degrade to an assignment, while this implementation
has the exception where `*this == *this.root_name()` and `p == preferred_seperator`
a normal append will be done, to allow:
```cpp
fs::path p1 = "//host/foo/bar/file.txt";
fs::path p2;
for (auto p : p1) p2 /= p;
ASSERT(p1 == p2);
```
For all non-host-leading paths the behaviour will match the one described by
the standard.
## Open Issues
### Windows
#### Symbolic Links on Windows
As symbolic links on Windows, while being supported more or less since
Windows Vista (with some strict security constraints) and fully since some earlier
build of Windows 10, when "Developer Mode" is activated, are at time of writing
(2018) rarely used, still they are supported with this implementation.
#### Permissions
The Windows ACL permission feature translates badly to the POSIX permission
bit mask used in the interface of C++17 filesystem. The permissions returned
in the `file_status` are therefore currently synthesized for the `user`-level
and copied to the `group`- and `other`-level. There is still some potential
for more interaction with the Windows permission system, but currently setting
or reading permissions with this implementation will most certainly not lead
to the expected behavior.
## Release Notes
### v1.5.3 (WIP)
* Fix for [#107](https://github.com/gulrak/filesystem/issues/107), the error
handling for status calls was suppressing errors on symlink targets.
* Pull request [#106](https://github.com/gulrak/filesystem/issues/106), fixed
detection of AppleClang for compile options.
* Pull request [#105](https://github.com/gulrak/filesystem/issues/105), added
option GHC_FILESYSTEM_BUILD_STD_TESTING to override additional build of
`std::filesystem` versions of the tests for comparison and the possibility
to use GHC_FILESYSTEM_TEST_COMPILE_FEATURES to prefill the used compile
features defaulting to CMAKE_CXX_COMPILE_FEATURES when not given.
### [v1.5.2](https://github.com/gulrak/filesystem/releases/tag/v1.5.2)
* Enhancement [#104](https://github.com/gulrak/filesystem/issues/104),
on POSIX backend: optimized reuse of status information and reduced
`directory_entry` creation leads to about 20%-25% in tests with
`recursive_directory_iterator` over a larger directory tree.
* Pull request [#103](https://github.com/gulrak/filesystem/pull/103), `wchar_t`
was not in the list of supported char types on non-Windows backends.
* Pull request [#102](https://github.com/gulrak/filesystem/pull/102), improved
`string_view` support makes use of `<string_view>` or `<experiemental/string_view>`
when available, and allows use of custom `basic_string_view` implementation
when defining `GHC_HAS_CUSTOM_STRING_VIEW` and importing the string view
into the `ghc::filesystem` namespace before including filesystem header.
* Pull request [#101](https://github.com/gulrak/filesystem/pull/101), fix for
[#100](https://github.com/gulrak/filesystem/issues/100), append and concat
type of operations on path called redundant conversions.
* Pull request [#98](https://github.com/gulrak/filesystem/pull/98), on older
linux variants (GCC 7/8), the comerative `std::filesystem` tests now link
with `-lrt` to avoid issues.
* Fix for [#97](https://github.com/gulrak/filesystem/issues/97), on BTRFS the
test case for `fs::hard_link_count` failed due to the filesystems behavior,
the test case was adapted to take that into account.
* Pull request [#96](https://github.com/gulrak/filesystem/pull/96), the export
attribute defines `GHC_FS_API` and `GHC_FS_API_CLASS` are now honored when when
set from outside to allow override of behavior.
* Fix for [#95](https://github.com/gulrak/filesystem/issues/95), the syntax for
disabling the deprecated warning in tests in MSVC was wrong.
* Pull request [#93](https://github.com/gulrak/filesystem/pull/93), now the
CMake configuration file is configured and part of the `make install` files.
### [v1.5.0](https://github.com/gulrak/filesystem/releases/tag/v1.5.0)
* Fix for [#91](https://github.com/gulrak/filesystem/issues/91), the way
the CMake build options `GHC_FILESYSTEM_BUILD_TESTING`, `GHC_FILESYSTEM_BUILD_EXAMPLES`
and `GHC_FILESYSTEM_WITH_INSTALL` where implemented, prohibited setting them
from a parent project when using this via `add_subdirectory`, this fix
allows to set them again.
* Major refactoring for [#90](https://github.com/gulrak/filesystem/issues/90),
the way, the Windows version of `fs::path` was originally created from the
POSIX based implementation was, by adaption of the incoming and outgoing
strings. This resulted in a mutable cache inside `fs::path`on Windows, that
was inherently not thread-safe, even for `const` methods.
To not add additional patches to a suboptimal solution, this time I reworked
the `path` code to now store _native_ path-representation. This changed a
lot of code, but when combined with `wchar_t` as `value_type` helped to avoid
lots of conversion for calls to Win-API.<br>
As interfaces where changed, it had to be released in a new minor version.
The set of refactorings resulted in the following changes:
* `fs::path::native()` and `fs::path::c_str()` can now be `noexcept` as the
standard mandates
* On Windows `wchar_t` is now the default for `fs::path::value_type` and
`std::wstring` is the default für `fs::path::string_type`.
* This allows the implementation to call Win-API without allocating
conversions
* Thread-safety on `const` methods of `fs::path` is no longer an issue
* Some code could be simplified during this refactoring
* Automatic prefixing of long path on Windows can now be disabled with
defining `GHC_WIN_DISABLE_AUTO_PREFIXES`, for all other types of prefixes
or namespaces the behavior follows that of MSVC `std::filesystem::path`
* In case the old `char`/`std::string` based approach for Windows is still
needed, it can be activated with `GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE`
* Enhancement for [#89](https://github.com/gulrak/filesystem/issues/89), `fs::file_status`
now supports `operator==` introduced in `std::filesystem` with C++20.
* Refactoring for [#88](https://github.com/gulrak/filesystem/issues/88), `fs::path::parent_path()`
had a performance issue, as it was still using a loop based approach to recreate
the parent from elements. This created lots of temporaries and was too slow
especially on long paths.
### [v1.4.0](https://github.com/gulrak/filesystem/releases/tag/v1.4.0)
* Enhancements for [#71](https://github.com/gulrak/filesystem/issues/71), when compiled with C++20:
* `char8_t` and `std::u8string` are supported where `Source` is the parameter type
* `fs::path::u8string()` and `fs::path::generic_u8string()` now return a `std::u8string`
* The _spaceship operator_ `<=>` is now supported for `fs::path`
* With the define `GHC_FILESYSTEM_ENFORCE_CPP17_API` `ghc::filesystem` will fall back
to the old `fs::path::u8string()` and `fs::path::generic_u8string()` API if preferred
* Bugfix for `fs::proximate(p, ec)` where the internal call to `fs::current_path()` was not
using the `error_code` variant, throwing possible exceptions instead of setting `ec`.
* Enhancement `LWG_2936_BEHAVIOUR` is now on by default.
* Some cleanup work to reduce preprocessor directives for better readability and remove unneeded
template specializations.
### [v1.3.10](https://github.com/gulrak/filesystem/releases/tag/v1.3.10)
* Fix for [#81](https://github.com/gulrak/filesystem/issues/81), fixed issues with
handling `Source` parameters that are string views.
* Fix for [#79](https://github.com/gulrak/filesystem/issues/79), the bit operations
for filesystem bitmasks that should be are now `constexpr`.
### [v1.3.8](https://github.com/gulrak/filesystem/releases/tag/v1.3.8)
* Refactoring for [#78](https://github.com/gulrak/filesystem/issues/78), the dynamic
switching helper includes are now using `__MAC_OS_X_VERSION_MIN_REQUIRED` to
ensure that `std::filesystem` is only selected on macOS if the deployment target is
at least Catalina.
* Bugfix for [#77](https://github.com/gulrak/filesystem/issues/77), the `directory_iterator`
and the `recursive_directory_iterator` had an issue with the `skip_permission_denied`
option, that leads to the inability to skip SIP protected folders on macOS.
* Enhancement for [#76](https://github.com/gulrak/filesystem/issues/76), `_MSVC_LANG` is
now used when available, additionally to `__cplusplus`, in the helping headers to
allow them to work even when `/Zc:__cplusplus` is not used.
* Bugfix for [#75](https://github.com/gulrak/filesystem/issues/75), NTFS reparse points
to mapped volumes where handled incorrect, leading to `false` on `fs::exists` or
not-found-errors on `fs::status`. Namespaced paths are not filtered anymore.
### [v1.3.6](https://github.com/gulrak/filesystem/releases/tag/v1.3.6)
* Pull request [#74](https://github.com/gulrak/filesystem/pull/74), on Windows symlink
evaluation used the wrong reparse struct information and was not handling the case
of relative paths well, thanks for the contribution.
* Refactoring for [#73](https://github.com/gulrak/filesystem/issues/73), enhanced performance
in path handling. the changes lead to much fewer path/string creations or copies, speeding
up large directory iteration or operations on many path instances.
* Bugfix for [#72](https://github.com/gulrak/filesystem/issues/72), the `TestAllocator` in
`filesystem_test.cpp` was completed to fulfill the requirements to build on CentOS 7 with
`devtoolset-9`. CentOS 7 and CentOS 8 are now part of the CI builds.
* Bugfix for [#70](https://github.com/gulrak/filesystem/issues/70), root names are now case
insensitive on Windows. This fix also adds the new behaviour switch `LWG_2936_BEHAVIOUR`
that allows to enable post C++17 `fs::path::compare` behaviour, where the comparison is as
if it was an element wise path comparison as described in
[LWG 2936](https://cplusplus.github.io/LWG/issue2936) and C++20 `[fs.path.compare]`.
It is default off in v1.3.6 and will be default starting from v1.4.0 as it changes ordering.
### [v1.3.4](https://github.com/gulrak/filesystem/releases/tag/v1.3.4)
* Pull request [#69](https://github.com/gulrak/filesystem/pull/69), use `wchar_t` versions of
`std::fstream` from `ghc::filesystem::fstream` wrappers on Windows if using GCC with libc++.
* Bugfix for [#68](https://github.com/gulrak/filesystem/issues/68), better handling of
permission issues for directory iterators when using `fs::directory_options::skip_permission_denied`
and initial support for compilation with emscripten.
* Refactoring for [#66](https://github.com/gulrak/filesystem/issues/63), unneeded shared_ptr guards
where removed and the file handles closed where needed to avoid unnecessary allocations.
* Bugfix for [#63](https://github.com/gulrak/filesystem/issues/63), fixed issues on Windows
with clang++ and C++17.
* Pull request [#62](https://github.com/gulrak/filesystem/pull/62), various fixes for
better Android support, thanks for the PR
* Pull request [#61](https://github.com/gulrak/filesystem/pull/61), `ghc::filesystem` now
supports use in projects with disabled exceptions. API signatures using exceptions for
error handling are not available in this mode, thanks for the PR (this resolves
[#60](https://github.com/gulrak/filesystem/issues/60) and
[#43](https://github.com/gulrak/filesystem/issues/43))
### [v1.3.2](https://github.com/gulrak/filesystem/releases/tag/v1.3.2)
* Bugfix for [#58](https://github.com/gulrak/filesystem/issues/58), on MinGW the
compilation could fail with an error about an undefined `ERROR_FILE_TOO_LARGE`
constant.
* Bugfix for [#56](https://github.com/gulrak/filesystem/issues/58), `fs::lexically_relative`
didn't ignore trailing slash on the base parameter, thanks for PR
[#57](https://github.com/gulrak/filesystem/pull/57).
* Bugfix for [#55](https://github.com/gulrak/filesystem/issues/55), `fs::create_directories`
returned `true` when nothing needed to be created, because the directory already existed.
* Bugfix for [#54](https://github.com/gulrak/filesystem/issues/54), `error_code`
was not reset, if cached result was returned.
* Pull request [#53](https://github.com/gulrak/filesystem/pull/53), fix for wrong
handling of leading whitespace when reading `fs::path` from a stream.
* Pull request [#52](https://github.com/gulrak/filesystem/pull/52), an ARM Linux
target is now part of the CI infrastructure with the service of Drone CI.
* Pull request [#51](https://github.com/gulrak/filesystem/pull/51), FreeBSD is now
part of the CI infrastucture with the service of Cirrus CI.
* Pull request [#50](https://github.com/gulrak/filesystem/pull/50), adaptive cast to
`timespec` fields to avoid warnings.
### [v1.3.0](https://github.com/gulrak/filesystem/releases/tag/v1.3.0)
* **Important: `ghc::filesystem` is re-licensed from BSD-3-Clause to MIT license.** (see
[#47](https://github.com/gulrak/filesystem/issues/47))
* Pull request [#46](https://github.com/gulrak/filesystem/pull/46), suppresses
unused parameter warning on Android.
* Bugfix for [#44](https://github.com/gulrak/filesystem/issues/44), fixes
for warnings from newer Xcode versions.
### [v1.2.10](https://github.com/gulrak/filesystem/releases/tag/v1.2.10)
* The Visual Studio 2019 compiler, GCC 9.2 and Clang 9.0 where added to the
CI configuration.
* Bugfix for [#41](https://github.com/gulrak/filesystem/issues/41), `fs::rename`
on Windows didn't replace an axisting regular file as required by the standard,
but gave an error. New tests and a fix as provided in the issue was implemented.
* Bugfix for [#39](https://github.com/gulrak/filesystem/issues/39), for the
forwarding use via `fs_fwd.hpp` or `fs_std_fwd.hpp` der was a use of
`DWORD` in the forwarding part leading to an error if `Windows.h` was not
included before the header. The tests were changed to give an error in that
case too and the useage of `DWORD` was removed.
* Bugfix for [#38](https://github.com/gulrak/filesystem/issues/38), casting the
return value of `GetProcAddress` gave a warning with `-Wcast-function-type`
on MSYS2 and MinGW GCC 9 builds.
### [v1.2.8](https://github.com/gulrak/filesystem/releases/tag/v1.2.8)
* Pull request [#30](https://github.com/gulrak/filesystem/pull/30), the
`CMakeLists.txt` will automatically exclude building examples and tests when
used as submodule, the configuration options now use a prefixed name to
reduce risk of conflicts.
* Pull request [#24](https://github.com/gulrak/filesystem/pull/24), install
target now creates a `ghcFilesystemConfig.cmake` in
`${CMAKE_INSTALL_LIBDIR}/cmake/ghcFilesystem` for `find_package` that
exports a target as `ghcFilesystem::ghc_filesystem`.
* Pull request [#31](https://github.com/gulrak/filesystem/pull/31), fixes
`error: redundant redeclaration of 'constexpr' static data member` deprecation
warning in C++17 mode.
* Pull request [#32](https://github.com/gulrak/filesystem/pull/32), fixes
old-style-cast warnings.
* Pull request [#34](https://github.com/gulrak/filesystem/pull/34), fixes
[TOCTOU](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use) situation
on `fs::create_directories`, thanks for the PR!
* Feature [#35](https://github.com/gulrak/filesystem/issues/35), new CMake
option to add an install target `GHC_FILESYSTEM_WITH_INSTALL` that is
defaulted to OFF if `ghc::filesystem` is used via `add_subdirectory`.
* Bugfix for [#33](https://github.com/gulrak/filesystem/issues/33), fixes
an issue with `fs::path::lexically_normal()` that leaves a trailing separator
in case of a resulting path ending with `..` as last element.
* Bugfix for [#36](https://github.com/gulrak/filesystem/issues/36), warings
on Xcode 11.2 due to unhelpfull references in path element iteration.
### [v1.2.6](https://github.com/gulrak/filesystem/releases/tag/v1.2.6)
* Pull request [#23](https://github.com/gulrak/filesystem/pull/23), tests and
examples can now be disabled in CMake via seting `BUILD_TESTING` and
`BUILD_EXAMPLES` to `NO`, `OFF` or `FALSE`.
* Pull request [#25](https://github.com/gulrak/filesystem/pull/25),
missing specialization for construction from `std::string_view` when
available was added.
* Additional test case when `std::string_view` is available.
* Bugfix for [#27](https://github.com/gulrak/filesystem/issues/27), the
`fs::path::preferred_seperator` declaration was not compiling on pre
C++17 compilers and no test accessed it, to show the problem. Fixed
it to an construction C++11 compiler should accept and added a test that
is successful on all combinations tested.
* Bugfix for [#29](https://github.com/gulrak/filesystem/issues/29), stricter
warning settings where chosen and resulting warnings where fixed.
### [v1.2.4](https://github.com/gulrak/filesystem/releases/tag/v1.2.4)
* Enabled stronger warning switches and resulting fixed issues on GCC and MinGW
* Bugfix for #22, the `fs::copy_options` where not forwarded from `fs::copy` to
`fs::copy_file` in one of the cases.
### [v1.2.2](https://github.com/gulrak/filesystem/releases/tag/v1.2.2)
* Fix for ([#21](https://github.com/gulrak/filesystem/pull/21)), when compiling
on Alpine Linux with musl instead of glibc, the wrong `strerror_r` signature
was expected. The complex preprocessor define mix was dropped in favor of
the usual dispatch by overloading a unifying wrapper.
### [v1.2.0](https://github.com/gulrak/filesystem/releases/tag/v1.2.0)
* Added MinGW 32/64 and Visual Studio 2015 builds to the CI configuration.
* Fixed additional compilation issues on MinGW.
* Pull request ([#13](https://github.com/gulrak/filesystem/pull/13)), set
minimum required CMake version to 3.7.2 (as in Debian 8).
* Pull request ([#14](https://github.com/gulrak/filesystem/pull/14)), added
support for a make install target.
* Bugfix for ([#15](https://github.com/gulrak/filesystem/issues/15)), the
forward/impl way of using `ghc::filesystem` missed a `<vector>` include
in the windows case.
* Bugfix for ([#16](https://github.com/gulrak/filesystem/issues/16)),
VS2019 didn't like the old size dispatching in the utf8 decoder, so it
was changed to a sfinae based approach.
* New feature ([#17](https://github.com/gulrak/filesystem/issues/17)), optional
support for standard conforming `wchar_t/std::wstring` interface when
compiling on Windows with defined `GHC_WIN_WSTRING_STRING_TYPE`, this is
default when using the `ghc/fs_std*.hpp` header, to enhance compatibility.
* New feature ([#18](https://github.com/gulrak/filesystem/issues/18)), optional
filesystem exceptions/errors on unicode errors with defined
`GHC_RAISE_UNICODE_ERRORS` (instead of replacing invalid code points or
UTF-8 encoding errors with the replacement character `U+FFFD`).
* Pull request ([#20](https://github.com/gulrak/filesystem/pull/20)), fix for
file handle leak in `fs::copy_file`.
* Coverage now checked in CI (~95% line coverage).
### [v1.1.4](https://github.com/gulrak/filesystem/releases/tag/v1.1.4)
* Additional Bugfix for ([#12](https://github.com/gulrak/filesystem/issues/12)),
error in old unified `readdir/readdir_r` code of `fs::directory_iterator`;
as `readdir_r` is now depricated, I decided to drop it and the resulting
code is much easier, shorter and due to more refactoring faster
* Fix for crashing unit tests against MSVC C++17 std::filesystem
* Travis-CI now additionally test with Xcode 10.2 on macOS
* Some minor refactorings
### [v1.1.2](https://github.com/gulrak/filesystem/releases/tag/v1.1.2)
* Bugfix for ([#11](https://github.com/gulrak/filesystem/issues/11)),
`fs::path::lexically_normal()` had some issues with `".."`-sequences.
* Bugfix for ([#12](https://github.com/gulrak/filesystem/issues/12)),
`fs::recursive_directory_iterator` could run into endless loops,
the methods depth() and pop() had issues and the copy behaviour and
`input_iterator_tag` conformance was broken, added tests
* Restructured some CMake code into a macro to ease the support for
C++17 std::filesystem builds of tests and examples for interoperability
checks.
* Some fixes on Windows tests to ease interoperability test runs.
* Reduced noise on `fs::weakly_canonical()` tests against `std::fs`
* Added simple `du` example showing the `recursive_directory_iterator`
used to add the sizes of files in a directory tree.
* Added error checking in `fs::file_time_type` test helpers
* `fs::copy()` now conforms LWG #2682, disallowing the use of
`copy_option::create_symlinks' to be used on directories
### [v1.1.0](https://github.com/gulrak/filesystem/releases/tag/v1.1.0)
* Restructuring of the project directory. The header files are now using
`hpp` as extension to be marked as c++ and they where moved to
`include/ghc/` to be able to include by `<ghc/filesystem.hpp>` as the
former include name might have been to generic and conflict with other
files.
* Better CMake support: `ghc::filesystem` now can be used as a submodul
and added with `add_subdirectory` and will export itself as `ghc_filesystem`
target. To use it, only `target_link_libraries(your-target ghc_filesystem)`
is needed and the include directories will be set so `#include <ghc/filesystem.hpp>`
will be a valid directive.
Still you can simply only add the header file to you project and include it
from there.
* Enhancement ([#10](https://github.com/gulrak/filesystem/issues/10)),
support for separation of implementation and forwarded api: Two
additional simple includes are added, that can be used to forward
`ghc::filesystem` declarations (`fs_fwd.hpp`) and to wrap the
implementation into a single cpp (`fs_impl.hpp`)
* The `std::basic_string_view` variants of the `fs::path` api are
now supported when compiling with C++17.
* Added CI integration for Travis-CI and Appveyor.
* Fixed MinGW compilation issues.
* Added long filename support for Windows.
### [v1.0.10](https://github.com/gulrak/filesystem/releases/tag/v1.0.10)
* Bugfix for ([#9](https://github.com/gulrak/filesystem/issues/9)), added
missing return statement to `ghc::filesystem::path::generic_string()`
* Added checks to hopefully better compile against Android NDK. There where
no tests run yet, so feedback is needed to actually call this supported.
* `filesystem.h` was renamed `filesystem.hpp` to better reflect that it is
a c++ language header.
### [v1.0.8](https://github.com/gulrak/filesystem/releases/tag/v1.0.8)
* Bugfix for ([#6](https://github.com/gulrak/filesystem/issues/6)), where
`ghc::filesystem::remove()` and `ghc::filesystem::remove_all()` both are
now able to remove a single file and both will not raise an error if the
path doesn't exist.
* Merged pull request ([#7](https://github.com/gulrak/filesystem/pull/7)),
a typo leading to setting error code instead of comparing it in
`ghc::filesystem::remove()` under Windows.
* Bugfix for (([#8](https://github.com/gulrak/filesystem/issues/8)), the
Windows version of `ghc::filesystem::directory_iterator` now releases
resources when reaching `end()` like the POSIX one does.
### [v1.0.6](https://github.com/gulrak/filesystem/releases/tag/v1.0.6)
* Bugfix for ([#4](https://github.com/gulrak/filesystem/issues/4)), missing error_code
propagation in `ghc::filesystem::copy()` and `ghc::filesystem::remove_all` fixed.
* Bugfix for ([#5](https://github.com/gulrak/filesystem/issues/5)), added missing std
namespace in `ghc::filesystem::recursive_directory_iterator::difference_type`.
### [v1.0.4](https://github.com/gulrak/filesystem/releases/tag/v1.0.4)
* Bugfix for ([#3](https://github.com/gulrak/filesystem/issues/3)), fixed missing inlines
and added test to ensure including into multiple implementation files works as expected.
* Building tests with `-Wall -Wextra -Werror` and fixed resulting issues.
### [v1.0.2](https://github.com/gulrak/filesystem/releases/tag/v1.0.2)
* Updated catch2 to v2.4.0.
* Refactored `fs.op.permissions` test to work with all tested `std::filesystem`
implementations (gcc, clang, msvc++).
* Added helper class `ghc::filesystem::u8arguments` as `argv` converter, to
help follow the UTF-8 path on windows. Simply instantiate it with `argc` and
`argv` and it will fetch the Unicode version of the command line and convert
it to UTF-8. The destructor reverts the change.
* Added `examples` folder with hopefully some usefull example usage. Examples are
tested (and build) with `ghc::filesystem` and C++17 `std::filesystem` when
available.
* Starting with this version, only even patch level versions will be tagged and
odd patch levels mark in-between non-stable wip states.
* Tests can now also be run against MS version of std::filesystem for comparison.
* Added missing `fstream` include.
* Removed non-conforming C99 `timespec`/`timeval` usage.
* Fixed some integer type mismatches that could lead to warnings.
* Fixed `chrono` conversion issues in test and example on clang 7.0.0.
### [v1.0.1](https://github.com/gulrak/filesystem/releases/tag/v1.0.1)
* Bugfix: `ghc::filesystem::canonical` now sees empty path as non-existant and reports
an error. Due to this `ghc::filesystem::weakly_canonical` now returns relative
paths for non-existant argument paths. ([#1](https://github.com/gulrak/filesystem/issues/1))
* Bugfix: `ghc::filesystem::remove_all` now also counts directories removed ([#2](https://github.com/gulrak/filesystem/issues/2))
* Bugfix: `recursive_directory_iterator` tests didn't respect equality domain issues
and dereferencable constraints, leading to fails on `std::filesystem` tests.
* Bugfix: Some `noexcept` tagged methods and functions could indirectly throw exceptions
due to UFT-8 decoding issues.
* `std_filesystem_test` is now also generated if LLVM/clang 7.0.0 is found.
### [v1.0.0](https://github.com/gulrak/filesystem/releases/tag/v1.0.0)
This was the first public release version. It implements the full range of
C++17 std::filesystem, as far as possible without other C++17 dependencies.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//---------------------------------------------------------------------------------------
// fs_fwd.hpp - The forwarding header for the header/implementation seperated usage of
// ghc::filesystem.
// This file can be include at any place, where ghc::filesystem api is needed while
// not bleeding implementation details (e.g. system includes) into the global namespace,
// as long as one cpp includes fs_impl.hpp to deliver the matching implementations.
//---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_FWD_H
#define GHC_FILESYSTEM_FWD_H
#define GHC_FILESYSTEM_FWD
#include <ghc/filesystem.hpp>
#endif // GHC_FILESYSTEM_FWD_H

View File

@ -0,0 +1,43 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//---------------------------------------------------------------------------------------
// fs_impl.hpp - The implementation header for the header/implementation seperated usage of
// ghc::filesystem.
// This file can be used to hide the implementation of ghc::filesystem into a single cpp.
// The cpp has to include this before including fs_fwd.hpp directly or via a different
// header to work.
//---------------------------------------------------------------------------------------
#define GHC_FILESYSTEM_IMPLEMENTATION
#include <ghc/filesystem.hpp>

View File

@ -0,0 +1,59 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//---------------------------------------------------------------------------------------
// fs_std.hpp - The dynamic switching header that includes std::filesystem if detected
// or ghc::filesystem if not, and makes the resulting API available in the
// namespace fs.
//---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_STD_H
#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include(<filesystem>)
#include <filesystem>
namespace fs {
using namespace std::filesystem;
using ifstream = std::ifstream;
using ofstream = std::ofstream;
using fstream = std::fstream;
}
#else
#include <ghc/filesystem.hpp>
namespace fs {
using namespace ghc::filesystem;
using ifstream = ghc::filesystem::ifstream;
using ofstream = ghc::filesystem::ofstream;
using fstream = ghc::filesystem::fstream;
}
#endif
#endif // GHC_FILESYSTEM_STD_H

View File

@ -0,0 +1,63 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//---------------------------------------------------------------------------------------
// fs_std_fwd.hpp - The forwarding header for the header/implementation seperated usage of
// ghc::filesystem that uses std::filesystem if it detects it.
// This file can be include at any place, where fs::filesystem api is needed while
// not bleeding implementation details (e.g. system includes) into the global namespace,
// as long as one cpp includes fs_std_impl.hpp to deliver the matching implementations.
//---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_STD_FWD_H
#define GHC_FILESYSTEM_STD_FWD_H
#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include(<filesystem>)
#include <filesystem>
namespace fs {
using namespace std::filesystem;
using ifstream = std::ifstream;
using ofstream = std::ofstream;
using fstream = std::fstream;
}
#else
#define GHC_FILESYSTEM_FWD
#include <ghc/filesystem.hpp>
namespace fs {
using namespace ghc::filesystem;
using ifstream = ghc::filesystem::ifstream;
using ofstream = ghc::filesystem::ofstream;
using fstream = ghc::filesystem::fstream;
}
#endif
#endif // GHC_FILESYSTEM_STD_FWD_H

View File

@ -0,0 +1,46 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//---------------------------------------------------------------------------------------
// fs_std_impl.hpp - The implementation header for the header/implementation seperated usage of
// ghc::filesystem that does nothing if std::filesystem is detected.
// This file can be used to hide the implementation of ghc::filesystem into a single cpp.
// The cpp has to include this before including fs_std_fwd.hpp directly or via a different
// header to work.
//---------------------------------------------------------------------------------------
#if !(defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include(<filesystem>))
#define GHC_FILESYSTEM_IMPLEMENTATION
#include <ghc/filesystem.hpp>
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,104 @@
#pragma once
#include <mw/common/Traits.h>
#include <boost/dynamic_bitset.hpp>
#include <serialize.h>
#include <vector>
struct BitSet : public Traits::ISerializable
{
boost::dynamic_bitset<> bitset;
BitSet() = default;
BitSet(const size_t size)
: bitset(size) { }
static BitSet From(const std::vector<uint8_t>& bytes)
{
BitSet ret;
ret.bitset.reserve(bytes.size() * 8);
for (uint8_t byte : bytes) {
for (uint8_t i = 0; i < 8; i++) {
ret.bitset.push_back(byte & (0x80 >> i));
}
}
return ret;
}
std::vector<uint8_t> bytes() const noexcept
{
std::vector<uint8_t> bytes((bitset.size() + 7) / 8);
for (size_t i = 0; i < bytes.size(); i++) {
for (uint8_t j = 0; j < 8; j++) {
size_t bit_index = (i * 8) + j;
if (bitset.size() > bit_index && bitset.test(bit_index)) {
bytes[i] |= (0x80 >> j);
}
}
}
return bytes;
}
std::string str() const noexcept
{
std::string val;
boost::to_string(bitset, val);
// Reverse for ascending order
return std::string(val.crbegin(), val.crend());
}
bool test(uint64_t idx) const noexcept { return bitset.size() > idx && bitset.test(idx); }
uint64_t count() const noexcept { return bitset.count(); }
uint64_t size() const noexcept { return bitset.size(); }
/// <summary>
/// Calculates the number of set bits that are smaller or equal to idx.
/// </summary>
/// <param name="idx">The index to calculate the rank for.</param>
/// <returns>The calculated rank.</returns>
uint64_t rank(uint64_t idx) const noexcept
{
uint64_t rank = 0;
for (uint64_t i = 0; i < idx; i++) {
if (i >= size()) {
break;
}
if (test(i)) {
++rank;
}
}
return rank;
}
void set(size_t idx, bool val = true) noexcept { bitset.set(idx, val); }
void set(size_t idx, size_t len, bool val) noexcept {
for (size_t i = idx; i < (idx + len); i++) {
bitset.set(i, val);
}
}
void push_back(bool val) noexcept { bitset.push_back(val); }
IMPL_SERIALIZED(BitSet);
template <typename Stream>
void Serialize(Stream& s) const
{
std::vector<uint8_t> vec = bytes();
::Serialize(s, vec);
}
template <typename Stream>
void Unserialize(Stream& s)
{
std::vector<uint8_t> vec;
::Unserialize(s, vec);
*this = BitSet::From(vec);
}
};

View File

@ -0,0 +1,220 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <memory>
#include <shared_mutex>
#include <mutex>
#include <tuple>
template<class T>
class Reader
{
template<class U>
class InnerReader
{
public:
InnerReader(std::shared_ptr<const U> pObject, std::shared_ptr<std::shared_timed_mutex> pMutex, const bool lock, const bool unlock)
: m_pObject(pObject), m_pMutex(pMutex), m_unlock(unlock)
{
if (lock)
{
m_pMutex->lock_shared();
}
}
~InnerReader()
{
if (m_unlock)
{
m_pMutex->unlock_shared();
}
}
std::shared_ptr<const U> m_pObject;
std::shared_ptr<std::shared_timed_mutex> m_pMutex;
bool m_unlock;
};
public:
static Reader Create(std::shared_ptr<T> pObject, std::shared_ptr<std::shared_timed_mutex> pMutex, const bool lock, const bool unlock)
{
return Reader(std::shared_ptr<InnerReader<T>>(new InnerReader<T>(pObject, pMutex, lock, unlock)));
}
Reader() = default;
virtual ~Reader() = default;
const T* operator->() const
{
return m_pReader->m_pObject.get();
}
const T& operator*() const
{
return *m_pReader->m_pObject;
}
std::shared_ptr<const T> GetShared() const
{
return m_pReader->m_pObject;
}
bool IsNull() const
{
return m_pReader->m_pObject == nullptr;
}
private:
Reader(std::shared_ptr<InnerReader<T>> pReader)
: m_pReader(pReader)
{
}
std::shared_ptr<InnerReader<T>> m_pReader;
};
class MutexUnlocker
{
public:
MutexUnlocker(std::shared_ptr<std::shared_timed_mutex> pMutex)
: m_pMutex(pMutex)
{
}
~MutexUnlocker()
{
m_pMutex->unlock();
}
private:
std::shared_ptr<std::shared_timed_mutex> m_pMutex;
};
template<class T>
class Writer : virtual public Reader<T>
{
template<class U>
class InnerWriter
{
public:
InnerWriter(std::shared_ptr<U> pObject, std::shared_ptr<std::shared_timed_mutex> pMutex, const bool lock)
: m_pObject(pObject), m_pMutex(pMutex)
{
if (lock)
{
m_pMutex->lock();
}
}
virtual ~InnerWriter()
{
// Using MutexUnlocker in case exception is thrown.
MutexUnlocker unlocker(m_pMutex);
}
std::shared_ptr<U> m_pObject;
std::shared_ptr<std::shared_timed_mutex> m_pMutex;
};
public:
static Writer Create(std::shared_ptr<T> pObject, std::shared_ptr<std::shared_timed_mutex> pMutex, const bool lock)
{
return Writer(std::shared_ptr<InnerWriter<T>>(new InnerWriter<T>(pObject, pMutex, lock)));
}
Writer() = default;
virtual ~Writer() = default;
T* operator->()
{
return m_pWriter->m_pObject.get();
}
const T* operator->() const
{
return m_pWriter->m_pObject.get();
}
T& operator*()
{
return *m_pWriter->m_pObject;
}
const T& operator*() const
{
return *m_pWriter->m_pObject;
}
std::shared_ptr<T> GetShared()
{
return m_pWriter->m_pObject;
}
std::shared_ptr<const T> GetShared() const
{
return m_pWriter->m_pObject;
}
bool IsNull() const
{
return m_pWriter == nullptr;
}
void Clear()
{
m_pWriter = nullptr;
}
private:
Writer(std::shared_ptr<InnerWriter<T>> pWriter)
: Reader<T>(Reader<T>::Create(pWriter->m_pObject, pWriter->m_pMutex, false, false)), m_pWriter(pWriter)
{
}
std::shared_ptr<InnerWriter<T>> m_pWriter;
};
template<class T>
class Locked
{
friend class MultiLocker;
public:
Locked(std::shared_ptr<T> pObject)
: m_pObject(pObject), m_pMutex(std::make_shared<std::shared_timed_mutex>())
{
}
virtual ~Locked() = default;
Reader<T> Read() const
{
return Reader<T>::Create(m_pObject, m_pMutex, true, true);
}
Reader<T> Read(std::adopt_lock_t) const
{
return Reader<T>::Create(m_pObject, m_pMutex, false, true);
}
Writer<T> Write()
{
return Writer<T>::Create(m_pObject, m_pMutex, true);
}
Writer<T> Write(std::adopt_lock_t)
{
return Writer<T>::Create(m_pObject, m_pMutex, false);
}
private:
std::shared_ptr<T> m_pObject;
std::shared_ptr<std::shared_timed_mutex> m_pMutex;
};

View File

@ -0,0 +1,71 @@
#pragma once
#include <mw/util/StringUtil.h>
#include <functional>
namespace LoggerAPI
{
enum LogLevel : uint8_t
{
NONE = 0,
TRACE = 1,
DEBUG = 2,
INFO = 3,
WARN = 4,
ERR = 5
};
void Initialize(const std::function<void(const std::string&)>& log_callback);
void Log(
const LoggerAPI::LogLevel log_level,
const std::string& function,
const size_t line,
const std::string& message
) noexcept;
}
template<typename ... Args>
static void LOG_F(
const LoggerAPI::LogLevel log_level,
const std::string& function,
const size_t line,
const char* format,
const Args& ... args) noexcept
{
try
{
std::string message = StringUtil::Format(format, args...);
LoggerAPI::Log(log_level, function, line, message);
}
catch (std::exception&)
{
// Logger failure should not disrupt program flow
}
}
// Node Logger
#define LOG_TRACE(message) LoggerAPI::Log(LoggerAPI::LogLevel::TRACE, __FUNCTION__, __LINE__, message)
#define LOG_DEBUG(message) LoggerAPI::Log(LoggerAPI::LogLevel::DEBUG, __FUNCTION__, __LINE__, message)
#define LOG_INFO(message) LoggerAPI::Log(LoggerAPI::LogLevel::INFO, __FUNCTION__, __LINE__, message)
#define LOG_WARNING(message) LoggerAPI::Log(LoggerAPI::LogLevel::WARN, __FUNCTION__, __LINE__, message)
#define LOG_ERROR(message) LoggerAPI::Log(LoggerAPI::LogLevel::ERR, __FUNCTION__, __LINE__, message)
#define LOG_TRACE_F(message, ...) LOG_F(LoggerAPI::LogLevel::TRACE, __FUNCTION__, __LINE__, message, __VA_ARGS__)
#define LOG_DEBUG_F(message, ...) LOG_F(LoggerAPI::LogLevel::DEBUG, __FUNCTION__, __LINE__, message, __VA_ARGS__)
#define LOG_INFO_F(message, ...) LOG_F(LoggerAPI::LogLevel::INFO, __FUNCTION__, __LINE__, message, __VA_ARGS__)
#define LOG_WARNING_F(message, ...) LOG_F(LoggerAPI::LogLevel::WARN, __FUNCTION__, __LINE__, message, __VA_ARGS__)
#define LOG_ERROR_F(message, ...) LOG_F(LoggerAPI::LogLevel::ERR, __FUNCTION__, __LINE__, message, __VA_ARGS__)
// Wallet Logger
#define WALLET_TRACE(message) LoggerAPI::Log(LoggerAPI::LogLevel::TRACE, __FUNCTION__, __LINE__, message)
#define WALLET_DEBUG(message) LoggerAPI::Log(LoggerAPI::LogLevel::DEBUG, __FUNCTION__, __LINE__, message)
#define WALLET_INFO(message) LoggerAPI::Log(LoggerAPI::LogLevel::INFO, __FUNCTION__, __LINE__, message)
#define WALLET_WARNING(message) LoggerAPI::Log(LoggerAPI::LogLevel::WARN, __FUNCTION__, __LINE__, message)
#define WALLET_ERROR(message) LoggerAPI::Log(LoggerAPI::LogLevel::ERR, __FUNCTION__, __LINE__, message)
#define WALLET_TRACE_F(message, ...) LOG_F(LoggerAPI::LogLevel::TRACE, __FUNCTION__, __LINE__, message, __VA_ARGS__)
#define WALLET_DEBUG_F(message, ...) LOG_F(LoggerAPI::LogLevel::DEBUG, __FUNCTION__, __LINE__, message, __VA_ARGS__)
#define WALLET_INFO_F(message, ...) LOG_F(LoggerAPI::LogLevel::INFO, __FUNCTION__, __LINE__, message, __VA_ARGS__)
#define WALLET_WARNING_F(message, ...) LOG_F(LoggerAPI::LogLevel::WARN, __FUNCTION__, __LINE__, message, __VA_ARGS__)
#define WALLET_ERROR_F(message, ...) LOG_F(LoggerAPI::LogLevel::ERR, __FUNCTION__, __LINE__, message, __VA_ARGS__)

View File

@ -0,0 +1,6 @@
#pragma once
#define MW_NAMESPACE namespace mw {
#define MMR_NAMESPACE namespace mmr {
#define TEST_NAMESPACE namespace test {
#define END_NAMESPACE }

View File

@ -0,0 +1,76 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <streams.h>
#include <version.h>
// Forward Declarations
template <size_t NUM_BYTES, class ALLOC>
class BigInt;
class Commitment;
namespace mw {
using Hash = BigInt<32, std::allocator<uint8_t>>;
}
#define IMPL_SERIALIZED(T) \
std::vector<uint8_t> Serialized() const noexcept final \
{ \
std::vector<uint8_t> serialized; \
CVectorWriter stream(SER_NETWORK, PROTOCOL_VERSION, serialized, 0); \
::Serialize(stream, *this); \
return serialized; \
} \
static T Deserialize(const std::vector<uint8_t>& serialized) \
{ \
T obj; \
VectorReader stream(SER_NETWORK, PROTOCOL_VERSION, serialized, 0); \
stream >> obj; \
return obj; \
}
#define IMPL_SERIALIZABLE(T, obj) \
IMPL_SERIALIZED(T) \
SERIALIZE_METHODS(T, obj)
namespace Traits
{
class ISerializable
{
public:
virtual ~ISerializable() = default;
//
// Serializes object into a byte vector.
//
virtual std::vector<uint8_t> Serialized() const noexcept = 0;
};
class IPrintable
{
public:
virtual ~IPrintable() = default;
virtual std::string Format() const = 0;
};
class ICommitted
{
public:
virtual ~ICommitted() = default;
virtual const Commitment& GetCommitment() const noexcept = 0;
};
class IHashable
{
public:
virtual ~IHashable() = default;
virtual const mw::Hash& GetHash() const noexcept = 0;
};
}

View File

@ -0,0 +1,66 @@
#pragma once
#include <mw/crypto/Pedersen.h>
#include <mw/models/tx/Transaction.h>
#include <cassert>
class Aggregation
{
public:
//
// Aggregates multiple transactions into 1.
//
static mw::Transaction::CPtr Aggregate(const std::vector<mw::Transaction::CPtr>& transactions)
{
if (transactions.empty()) {
return std::make_shared<mw::Transaction>();
}
if (transactions.size() == 1) {
return transactions.front();
}
std::vector<Input> inputs;
std::vector<Output> outputs;
std::vector<Kernel> kernels;
std::vector<BlindingFactor> kernel_offsets;
std::vector<BlindingFactor> stealth_offsets;
// collect all the inputs, outputs, kernels, and owner sigs from the txs
for (const mw::Transaction::CPtr& pTransaction : transactions) {
inputs.insert(
inputs.end(),
pTransaction->GetInputs().begin(),
pTransaction->GetInputs().end()
);
outputs.insert(
outputs.end(),
pTransaction->GetOutputs().begin(),
pTransaction->GetOutputs().end()
);
kernels.insert(
kernels.end(),
pTransaction->GetKernels().begin(),
pTransaction->GetKernels().end()
);
kernel_offsets.push_back(pTransaction->GetKernelOffset());
stealth_offsets.push_back(pTransaction->GetStealthOffset());
}
// Sum the offsets up to give us an aggregate offsets for the transaction.
BlindingFactor kernel_offset = Pedersen::AddBlindingFactors(kernel_offsets);
BlindingFactor stealth_offset = Pedersen::AddBlindingFactors(stealth_offsets);
// Build a new aggregate tx
return mw::Transaction::Create(
std::move(kernel_offset),
std::move(stealth_offset),
std::move(inputs),
std::move(outputs),
std::move(kernels)
);
}
};

View File

@ -0,0 +1,111 @@
#pragma once
#include <mw/exceptions/ValidationException.h>
#include <mw/crypto/Pedersen.h>
#include <mw/models/tx/TxBody.h>
#include <mw/models/tx/Transaction.h>
#include <mw/models/tx/UTXO.h>
#include <mw/common/Logger.h>
#include <cstdlib>
class KernelSumValidator
{
public:
// Makes sure the sums of all utxo commitments minus the total supply
// equals the sum of all kernel excesses and the total offset.
// This is to be used only when validating the entire state.
//
// Throws a ValidationException if the utxo sum != kernel sum.
static void ValidateState(
const std::vector<Commitment>& utxo_commitments,
const std::vector<Kernel>& kernels,
const BlindingFactor& total_offset)
{
// Sum all utxo commitments - expected supply.
int64_t total_mweb_supply = 0;
for (const Kernel& kernel : kernels) {
total_mweb_supply += kernel.GetSupplyChange();
// Total supply can never go below 0
if (total_mweb_supply < 0) {
ThrowValidation(EConsensusError::BLOCK_SUMS);
}
}
ValidateSums(
{},
utxo_commitments,
Commitments::From(kernels),
total_offset,
total_mweb_supply
);
}
static void ValidateForBlock(
const TxBody& body,
const BlindingFactor& total_offset,
const BlindingFactor& prev_total_offset)
{
BlindingFactor block_offset = total_offset;
if (!prev_total_offset.IsZero()) {
block_offset = Pedersen::AddBlindingFactors({block_offset}, {prev_total_offset});
}
ValidateSums(
body.GetInputCommits(),
body.GetOutputCommits(),
body.GetKernelCommits(),
block_offset,
body.GetSupplyChange()
);
}
static void ValidateForTx(const mw::Transaction& tx)
{
ValidateSums(
tx.GetInputCommits(),
tx.GetOutputCommits(),
tx.GetKernelCommits(),
tx.GetKernelOffset(),
tx.GetSupplyChange()
);
}
private:
static void ValidateSums(
const std::vector<Commitment>& input_commits,
const std::vector<Commitment>& output_commits,
const std::vector<Commitment>& kernel_commits,
const BlindingFactor& offset,
const int64_t coins_added)
{
// Calculate UTXO nonce sum
Commitment sum_utxo_commitment = Pedersen::AddCommitments(output_commits, input_commits);
if (coins_added > 0) {
sum_utxo_commitment = Pedersen::AddCommitments(
{ sum_utxo_commitment }, { Commitment::Transparent(coins_added) }
);
} else if (coins_added < 0) {
sum_utxo_commitment = Pedersen::AddCommitments(
{ sum_utxo_commitment, Commitment::Transparent(std::abs(coins_added)) }
);
}
// Calculate total kernel excess
Commitment sum_excess_commitment = Pedersen::AddCommitments(kernel_commits);
if (!offset.IsZero()) {
sum_excess_commitment = Pedersen::AddCommitments(
{ sum_excess_commitment, Commitment::Blinded(offset, 0) }
);
}
if (sum_utxo_commitment != sum_excess_commitment) {
LOG_ERROR_F(
"UTXO sum {} does not match kernel excess sum {}.",
sum_utxo_commitment,
sum_excess_commitment
);
ThrowValidation(EConsensusError::BLOCK_SUMS);
}
}
};

View File

@ -0,0 +1,15 @@
#pragma once
#include <mw/common/Macros.h>
#include <cstddef>
#include <cstdint>
MW_NAMESPACE
/// <summary>
/// Consensus parameters
/// Any change to these will cause a hardfork!
/// </summary>
static constexpr std::size_t MAX_BLOCK_WEIGHT = 21'000;
END_NAMESPACE

View File

@ -0,0 +1,63 @@
#pragma once
#include <mw/crypto/PublicKeys.h>
#include <mw/exceptions/ValidationException.h>
#include <mw/models/crypto/BlindingFactor.h>
#include <mw/models/tx/TxBody.h>
class StealthSumValidator
{
public:
//
// Verifies that stealth excesses balance:
//
// sum(K_s) + sum(K_i) - sum(K_o) = sum(E') + x'*G
//
static void Validate(const BlindingFactor& stealth_offset, const TxBody& body)
{
std::vector<PublicKey> lhs_keys;
//
// sum(K_s) + sum(K_i)
//
std::transform(
body.GetOutputs().cbegin(), body.GetOutputs().cend(), std::back_inserter(lhs_keys),
[](const Output& output) { return output.GetSenderPubKey(); }
);
std::transform(
body.GetInputs().cbegin(), body.GetInputs().cend(), std::back_inserter(lhs_keys),
[](const Input& input) { return input.GetInputPubKey(); }
);
PublicKey lhs_total;
if (!lhs_keys.empty()) {
lhs_total = PublicKeys::Add(lhs_keys);
}
//
// sum(E') + x'*G + sum(K_o)
//
std::vector<PublicKey> rhs_keys = body.GetStealthExcesses();
std::transform(
body.GetInputs().cbegin(), body.GetInputs().cend(), std::back_inserter(rhs_keys),
[](const Input& input) { return input.GetOutputPubKey(); }
);
if (!stealth_offset.IsZero()) {
rhs_keys.push_back(PublicKeys::Calculate(stealth_offset.GetBigInt()));
}
PublicKey rhs_total;
if (!rhs_keys.empty()) {
rhs_total = PublicKeys::Add(rhs_keys);
}
// sum(K_s) + sum(K_i) = sum(E') + x'*G + sum(K_o)
if (lhs_total != rhs_total) {
ThrowValidation(EConsensusError::STEALTH_SUMS);
}
}
};

View File

@ -0,0 +1,97 @@
#pragma once
#include <mw/consensus/Params.h>
#include <mw/models/tx/TxBody.h>
#include <numeric>
#include <utility>
class Weight
{
public:
static constexpr size_t BYTES_PER_WEIGHT = 42;
static constexpr size_t BASE_KERNEL_WEIGHT = 2;
static constexpr size_t STEALTH_EXCESS_WEIGHT = 1;
static constexpr size_t KERNEL_WITH_STEALTH_WEIGHT = BASE_KERNEL_WEIGHT + STEALTH_EXCESS_WEIGHT;
static constexpr size_t BASE_OUTPUT_WEIGHT = 17;
static constexpr size_t STANDARD_OUTPUT_FIELDS_WEIGHT = 1;
static constexpr size_t STANDARD_OUTPUT_WEIGHT = BASE_OUTPUT_WEIGHT + STANDARD_OUTPUT_FIELDS_WEIGHT;
static constexpr size_t MAX_NUM_INPUTS = 50'000;
static constexpr size_t INPUT_BYTES = 195;
static constexpr size_t MAX_MWEB_BYTES = 180 + (3 * 5) + // 180 bytes per header and 5 bytes each for input, output, and kernel vec size
(MAX_NUM_INPUTS * INPUT_BYTES) + // 50k inputs at 195 bytes each
(mw::MAX_BLOCK_WEIGHT * 60); // Ignoring inputs, no tx component is ever more than 60 bytes per unit of weight
static size_t Calculate(const TxBody& tx_body)
{
size_t kernel_weight = std::accumulate(
tx_body.GetKernels().begin(), tx_body.GetKernels().end(), (size_t)0,
[](size_t sum, const Kernel& kernel) {
size_t kern_weight = CalcKernelWeight(
kernel.HasStealthExcess(),
kernel.GetPegOuts(),
kernel.GetExtraData()
);
return sum + kern_weight;
}
);
size_t output_weight = std::accumulate(
tx_body.GetOutputs().begin(), tx_body.GetOutputs().end(), (size_t)0,
[](size_t sum, const Output& output) {
return sum + CalcOutputWeight(output.HasStandardFields(), output.GetExtraData());
}
);
return kernel_weight + output_weight;
}
static bool ExceedsMaximum(const TxBody& tx_body)
{
return tx_body.GetInputs().size() > MAX_NUM_INPUTS || Calculate(tx_body) > mw::MAX_BLOCK_WEIGHT;
}
static size_t CalcKernelWeight(
const bool has_stealth_excess,
const CScript& pegout_script = CScript{},
const std::vector<uint8_t>& extra_data = {})
{
// Kernels with a stealth excess cost extra.
size_t base_weight = has_stealth_excess ? KERNEL_WITH_STEALTH_WEIGHT : BASE_KERNEL_WEIGHT;
return base_weight + ExtraBytesToWeight(pegout_script.size()) + ExtraBytesToWeight(extra_data.size());
}
static size_t CalcKernelWeight(
const bool has_stealth_excess,
const std::vector<PegOutCoin>& pegouts = {},
const std::vector<uint8_t>& extra_data = {})
{
// Kernels with a stealth excess cost extra.
size_t base_weight = has_stealth_excess ? KERNEL_WITH_STEALTH_WEIGHT : BASE_KERNEL_WEIGHT;
size_t pegout_weight = std::accumulate(
pegouts.begin(), pegouts.end(), (size_t)0,
[](size_t sum, const PegOutCoin& pegout) {
return sum + ExtraBytesToWeight(pegout.GetScriptPubKey().size());
}
);
return base_weight + pegout_weight + ExtraBytesToWeight(extra_data.size());
}
// Outputs have a weight of 'BASE_OUTPUT_WEIGHT' plus 2 weight for standard fields, and 1 weight for every 'BYTES_PER_WEIGHT' extra_data bytes
static size_t CalcOutputWeight(const bool standard_fields, const std::vector<uint8_t>& extra_data = {})
{
size_t base_weight = standard_fields ? STANDARD_OUTPUT_WEIGHT : BASE_OUTPUT_WEIGHT;
return base_weight + ExtraBytesToWeight(extra_data.size());
}
private:
static size_t ExtraBytesToWeight(const size_t extra_bytes)
{
return (extra_bytes + (BYTES_PER_WEIGHT - 1)) / BYTES_PER_WEIGHT;
}
};

View File

@ -0,0 +1,86 @@
#pragma once
#include <mw/crypto/Pedersen.h>
class Blinds
{
public:
Blinds() = default;
Blinds(BlindingFactor blind)
: m_positive{std::move(blind)}, m_negative{} { }
static Blinds From(BlindingFactor blind) { return Blinds(std::move(blind)); }
static Blinds From(const SecretKey& key) { return Blinds(BlindingFactor(key)); }
Blinds& Add(BlindingFactor blind)
{
m_positive.push_back(std::move(blind));
return *this;
}
Blinds& Add(const BigInt<32>& blind)
{
m_positive.push_back(BlindingFactor(blind));
return *this;
}
Blinds& Add(const SecretKey& key)
{
m_positive.push_back(BlindingFactor(key));
return *this;
}
Blinds& Add(const std::vector<BlindingFactor>& blinds)
{
m_positive.insert(m_positive.end(), blinds.cbegin(), blinds.cend());
return *this;
}
Blinds& Add(const std::vector<SecretKey>& keys)
{
std::transform(
keys.cbegin(), keys.cend(), std::back_inserter(m_positive),
[](const SecretKey& key) { return BlindingFactor(key.GetBigInt()); });
return *this;
}
Blinds& Sub(BlindingFactor blind)
{
m_negative.push_back(std::move(blind));
return *this;
}
Blinds& Sub(const BigInt<32>& blind)
{
m_negative.push_back(BlindingFactor(blind));
return *this;
}
Blinds& Sub(const SecretKey& key)
{
m_negative.push_back(BlindingFactor(key.GetBigInt()));
return *this;
}
Blinds& Sub(const std::vector<BlindingFactor>& blinds)
{
m_negative.insert(m_negative.end(), blinds.cbegin(), blinds.cend());
return *this;
}
Blinds& Sub(const std::vector<SecretKey>& keys)
{
std::transform(
keys.cbegin(), keys.cend(), std::back_inserter(m_negative),
[](const SecretKey& key) { return BlindingFactor(key.GetBigInt()); }
);
return *this;
}
BlindingFactor Total() const { return Pedersen::AddBlindingFactors(m_positive, m_negative); }
SecretKey ToKey() const { return Pedersen::AddBlindingFactors(m_positive, m_negative).data(); }
private:
std::vector<BlindingFactor> m_positive;
std::vector<BlindingFactor> m_negative;
};

View File

@ -0,0 +1,33 @@
#pragma once
#include <mw/models/crypto/Commitment.h>
#include <mw/models/crypto/ProofData.h>
#include <mw/models/crypto/ProofMessage.h>
#include <mw/models/crypto/RangeProof.h>
#include <mw/models/crypto/RewoundProof.h>
#include <mw/models/crypto/SecretKey.h>
#include <memory>
class Bulletproofs
{
public:
static bool BatchVerify(
const std::vector<ProofData>& rangeProofs
);
static RangeProof::CPtr Generate(
const uint64_t amount,
const SecretKey& key,
const SecretKey& privateNonce,
const SecretKey& rewindNonce,
const ProofMessage& proofMessage,
const std::vector<uint8_t>& extraData
);
static std::unique_ptr<RewoundProof> Rewind(
const Commitment& commitment,
const RangeProof& rangeProof,
const std::vector<uint8_t>& extraData,
const SecretKey& nonce
);
};

View File

@ -0,0 +1,78 @@
#pragma once
#include <mw/common/Traits.h>
#include <mw/models/crypto/Hash.h>
#include <crypto/blake3/blake3.h>
#include <hash.h>
enum class EHashTag : char
{
ADDRESS = 'A',
BLIND = 'B',
DERIVE = 'D',
NONCE = 'N',
OUT_KEY = 'O',
SEND_KEY = 'S',
TAG = 'T',
NONCE_MASK = 'X',
VALUE_MASK = 'Y'
};
class Hasher
{
public:
Hasher()
{
blake3_hasher_init(&m_hasher);
}
Hasher(const EHashTag tag)
{
blake3_hasher_init(&m_hasher);
char c_tag = static_cast<char>(tag);
write(&c_tag, 1);
}
int GetType() const { return SER_GETHASH; }
int GetVersion() const { return 0; }
void write(const char* pch, size_t size)
{
blake3_hasher_update(&m_hasher, pch, size);
}
mw::Hash hash()
{
mw::Hash hashed;
blake3_hasher_finalize(&m_hasher, hashed.data(), hashed.size());
return hashed;
}
template <class T>
Hasher& Append(const T& t)
{
::Serialize(*this, t);
return *this;
}
template <typename T>
Hasher& operator<<(const T& obj)
{
// Serialize to this stream
::Serialize(*this, obj);
return (*this);
}
private:
blake3_hasher m_hasher;
};
extern mw::Hash Hashed(const std::vector<uint8_t>& serialized);
extern mw::Hash Hashed(const Traits::ISerializable& serializable);
template<class T>
mw::Hash Hashed(const EHashTag tag, const T& serializable)
{
return Hasher(tag).Append(serializable).hash();
}

View File

@ -0,0 +1,61 @@
#pragma once
#include <mw/crypto/PublicKeys.h>
#include <mw/models/crypto/BlindingFactor.h>
#include <mw/models/crypto/Commitment.h>
#include <mw/models/crypto/PublicKey.h>
#include <mw/models/crypto/SecretKey.h>
// FUTURE: Make this more efficient
class Keys
{
public:
static Keys From(const PublicKey& public_key)
{
return Keys(public_key);
}
static Keys From(const SecretKey& secret_key)
{
return Keys(PublicKey::From(secret_key));
}
static Keys Random()
{
return Keys(PublicKey::From(SecretKey::Random()));
}
const PublicKey& PubKey() const noexcept { return m_pubkey; }
Keys& Add(const PublicKey& public_key)
{
m_pubkey = PublicKeys::Add({m_pubkey, public_key});
return *this;
}
Keys& Add(const SecretKey& secret_key)
{
m_pubkey = PublicKeys::Add({m_pubkey, PublicKey::From(secret_key)});
return *this;
}
Keys& Add(const std::vector<PublicKey>& public_keys)
{
std::vector<PublicKey> pubkeys = public_keys;
pubkeys.push_back(m_pubkey);
m_pubkey = PublicKeys::Add(pubkeys);
return *this;
}
Keys& Mul(const SecretKey& secret_key)
{
m_pubkey = PublicKeys::MultiplyKey(m_pubkey, secret_key);
return *this;
}
private:
Keys(const PublicKey& pubkey)
: m_pubkey(pubkey) { }
PublicKey m_pubkey;
};

View File

@ -0,0 +1,45 @@
#pragma once
#include <mw/models/crypto/SecretKey.h>
#include <mw/models/crypto/Signature.h>
#include <mw/models/crypto/PublicKey.h>
#include <mw/models/crypto/Hash.h>
class MuSig
{
public:
static SecretKey GenerateSecureNonce();
//
// Builds one party's share of a Schnorr signature.
// Returns a CompactSignature if successful.
//
static CompactSignature CalculatePartial(
const SecretKey& secretKey,
const SecretKey& secretNonce,
const PublicKey& sumPubKeys,
const PublicKey& sumPubNonces,
const mw::Hash& message
);
//
// Verifies one party's share of a Schnorr signature.
// Returns true if valid.
//
static bool VerifyPartial(
const CompactSignature& partialSignature,
const PublicKey& publicKey,
const PublicKey& sumPubKeys,
const PublicKey& sumPubNonces,
const mw::Hash& message
);
//
// Combines multiple partial signatures to build the final aggregate signature.
// Returns the raw aggregate signature.
//
static Signature Aggregate(
const std::vector<CompactSignature>& signatures,
const PublicKey& sumPubNonces
);
};

View File

@ -0,0 +1,54 @@
#pragma once
#include <mw/models/crypto/BlindingFactor.h>
#include <mw/models/crypto/Commitment.h>
class Pedersen
{
public:
//
// Creates a pedersen commitment from a value with a zero blinding factor.
//
static Commitment CommitTransparent(const uint64_t value);
//
// Creates a pedersen commitment from a value with the supplied blinding factor.
//
static Commitment Commit(
const uint64_t value,
const BlindingFactor& blindingFactor
);
//
// Adds the homomorphic pedersen commitments together.
//
static Commitment AddCommitments(
const std::vector<Commitment>& positive,
const std::vector<Commitment>& negative = {}
);
//
// Adds blinding factors together (first negating those in the "negative" vector)
//
static BlindingFactor AddBlindingFactors(
const std::vector<BlindingFactor>& positive,
const std::vector<BlindingFactor>& negative = { }
);
//
// Calculates the blinding factor x' = x + SHA256(xG+vH | xJ), used in the switch commitment x'G+vH.
//
static BlindingFactor BlindSwitch(
const BlindingFactor& secretKey,
const uint64_t amount
);
private:
static Commitment PedersenCommitSum(
const std::vector<Commitment>& positive,
const std::vector<Commitment>& negative);
static BlindingFactor PedersenBlindSum(
const std::vector<BlindingFactor>& positive,
const std::vector<BlindingFactor>& negative);
};

View File

@ -0,0 +1,38 @@
#pragma once
#include <mw/models/crypto/BigInteger.h>
#include <mw/models/crypto/SecretKey.h>
#include <mw/models/crypto/PublicKey.h>
class PublicKeys
{
public:
//
// Calculates the 33 byte public key from the 32 byte private key using curve secp256k1.
//
static PublicKey Calculate(const BigInt<32>& privateKey);
//
// Converts curve point from Pedersen Commitment serialization to standard PublicKey serialization.
//
static PublicKey Convert(const Commitment& commitment);
//
// Adds a number of public keys together.
// Returns the combined public key if successful.
//
static PublicKey Add(
const std::vector<PublicKey>& publicKeys,
const std::vector<PublicKey>& subtract = {}
);
//
// Multiplies the public key (curve point) by given scalar.
//
static PublicKey MultiplyKey(const PublicKey& public_key, const SecretKey& mul);
//
// Multiplies the public key (curve point) by the inverse of the given scalar.
//
static PublicKey DivideKey(const PublicKey& public_key, const SecretKey& div);
};

View File

@ -0,0 +1,36 @@
#pragma once
#include <mw/models/crypto/Commitment.h>
#include <mw/models/crypto/SecretKey.h>
#include <mw/models/crypto/Signature.h>
#include <mw/models/crypto/PublicKey.h>
#include <mw/models/crypto/Hash.h>
#include <mw/models/crypto/SignedMessage.h>
class Schnorr
{
public:
//
// Signs the message hash with the given key.
// If successful, returns a schnorr signature.
//
static Signature Sign(
const uint8_t* secretKey,
const mw::Hash& message
);
static SignedMessage SignMessage(
const SecretKey& secretKey,
const mw::Hash& message
);
static bool Verify(
const Signature& signature,
const PublicKey& sumPubKeys,
const mw::Hash& message
);
static bool BatchVerify(
const std::vector<SignedMessage>& signatures
);
};

View File

@ -0,0 +1,25 @@
#pragma once
#include <mw/models/crypto/SecretKey.h>
class SecretKeys
{
public:
static SecretKeys From(const SecretKey& secret_key);
static SecretKeys Random()
{
return SecretKeys::From(SecretKey::Random());
}
const SecretKey& Total() const noexcept { return m_key; }
SecretKeys& Add(const SecretKey& secret_key);
SecretKeys& Mul(const SecretKey& secret_key);
private:
SecretKeys(const SecretKey& key)
: m_key(key) {}
SecretKey m_key;
};

View File

@ -0,0 +1,46 @@
#pragma once
#include <mw/models/tx/UTXO.h>
#include <mw/interfaces/db_interface.h>
#include <unordered_map>
// Forward Declarations
class Database;
class CoinDB
{
public:
using UPtr = std::unique_ptr<CoinDB>;
CoinDB(mw::DBWrapper* pDBWrapper, mw::DBBatch* pBatch = nullptr);
~CoinDB();
//
// Retrieve UTXOs with matching output IDs.
// If there are multiple UTXOs for an output ID, the most recent will be returned.
//
std::unordered_map<mw::Hash, UTXO::CPtr> GetUTXOs(
const std::vector<mw::Hash>& output_ids
) const;
//
// Add the UTXOs
//
void AddUTXOs(const std::vector<UTXO::CPtr>& utxos);
//
// Removes the UTXOs for the given output IDs.
// If there are multiple UTXOs for an output ID, the most recent will be removed.
// DatabaseException thrown if no UTXOs are found for an output ID.
//
void RemoveUTXOs(const std::vector<mw::Hash>& output_ids);
//
// Removes all of the UTXOs from the database.
// This is useful when resyncing the chain.
//
void RemoveAllUTXOs();
private:
std::unique_ptr<Database> m_pDatabase;
};

View File

@ -0,0 +1,24 @@
#pragma once
#include <mw/mmr/Leaf.h>
#include <mw/models/crypto/Hash.h>
#include <mw/interfaces/db_interface.h>
// Forward Declarations
class Database;
class LeafDB
{
public:
LeafDB(const char prefix, mw::DBWrapper* pDBWrapper, mw::DBBatch* pBatch = nullptr);
~LeafDB();
std::unique_ptr<mmr::Leaf> Get(const mmr::LeafIndex& idx) const;
void Add(const std::vector<mmr::Leaf>& leaves);
void Remove(const std::vector<mmr::LeafIndex>& indices);
void RemoveAll();
private:
char m_prefix;
std::unique_ptr<Database> m_pDatabase;
};

View File

@ -0,0 +1,36 @@
#pragma once
#include <mw/mmr/MMRInfo.h>
#include <mw/interfaces/db_interface.h>
// Forward Declarations
class Database;
class MMRInfoDB
{
public:
MMRInfoDB(mw::DBWrapper* pDBWrapper, mw::DBBatch* pBatch = nullptr);
~MMRInfoDB();
/// <summary>
/// Retrieves an MMRInfo by index.
/// </summary>
/// <param name="index">The index of the MMRInfo to retrieve.</param>
/// <returns>The MMRInfo for the given index. nullptr if not found.</returns>
std::unique_ptr<MMRInfo> GetByIndex(const uint32_t index) const;
/// <summary>
/// Retrieves the latest MMRInfo.
/// </summary>
/// <returns>The latest MMRInfo. nullptr if none are found.</returns>
std::unique_ptr<MMRInfo> GetLatest() const;
/// <summary>
/// Saves by index and also saves the entry as the latest.
/// </summary>
/// <param name="info">The MMR info to save</param>
void Save(const MMRInfo& info);
private:
std::unique_ptr<Database> m_pDatabase;
};

View File

@ -0,0 +1,17 @@
#pragma once
#include <mw/exceptions/LTCException.h>
#include <mw/util/StringUtil.h>
#define ThrowCrypto(msg) throw CryptoException(msg, __FUNCTION__)
#define ThrowCrypto_F(msg, ...) throw CryptoException(StringUtil::Format(msg, __VA_ARGS__), __FUNCTION__)
class CryptoException : public LTCException
{
public:
CryptoException(const std::string& message, const std::string& function)
: LTCException("CryptoException", message, function)
{
}
};

View File

@ -0,0 +1,17 @@
#pragma once
#include <mw/exceptions/LTCException.h>
#include <mw/util/StringUtil.h>
#define ThrowDatabase(msg) throw DatabaseException(msg, __FUNCTION__)
#define ThrowDatabase_F(msg, ...) throw DatabaseException(StringUtil::Format(msg, __VA_ARGS__), __FUNCTION__)
class DatabaseException : public LTCException
{
public:
DatabaseException(const std::string& message, const std::string& function)
: LTCException("DatabaseException", message, function)
{
}
};

View File

@ -0,0 +1,17 @@
#pragma once
#include <mw/exceptions/LTCException.h>
#include <mw/util/StringUtil.h>
#define ThrowDeserialization(msg) throw DeserializationException(msg, __FUNCTION__)
#define ThrowDeserialization_F(msg, ...) throw DeserializationException(StringUtil::Format(msg, __VA_ARGS__), __FUNCTION__)
class DeserializationException : public LTCException
{
public:
DeserializationException(const std::string& message, const std::string& function)
: LTCException("DeserializationException", message, function)
{
}
};

View File

@ -0,0 +1,17 @@
#pragma once
#include <mw/exceptions/LTCException.h>
#include <mw/util/StringUtil.h>
#define ThrowFile(msg) throw FileException(msg, __FUNCTION__)
#define ThrowFile_F(msg, ...) throw FileException(StringUtil::Format(msg, __VA_ARGS__), __FUNCTION__)
class FileException : public LTCException
{
public:
FileException(const std::string& message, const std::string& function)
: LTCException("FileException", message, function)
{
}
};

View File

@ -0,0 +1,17 @@
#pragma once
#include <mw/exceptions/LTCException.h>
#include <mw/util/StringUtil.h>
#define ThrowInsufficientFunds(msg) throw InsufficientFundsException(msg, __FUNCTION__)
#define ThrowInsufficientFunds_F(msg, ...) throw InsufficientFundsException(StringUtil::Format(msg, __VA_ARGS__), __FUNCTION__)
class InsufficientFundsException : public LTCException
{
public:
InsufficientFundsException(const std::string& message, const std::string& function)
: LTCException("InsufficientFundsException", message, function)
{
}
};

View File

@ -0,0 +1,33 @@
#pragma once
#include <stdexcept>
#include <string>
class LTCException : public std::runtime_error
{
public:
~LTCException() = default;
const char* what() const noexcept override
{
return m_what.c_str();
}
const std::string& GetMsg() const { return m_message; }
protected:
LTCException(const std::string& type, const std::string& message, const std::string& function)
: std::runtime_error(function + ": " + message)
{
m_type = type;
m_message = message;
m_function = function;
m_what = function + ": " + message;
}
private:
std::string m_type;
std::string m_message;
std::string m_function;
std::string m_what;
};

View File

@ -0,0 +1,17 @@
#pragma once
#include <mw/exceptions/LTCException.h>
#include <mw/util/StringUtil.h>
#define ThrowNotFound(msg) throw NotFoundException(msg, __FUNCTION__)
#define ThrowNotFound_F(msg, ...) throw NotFoundException(StringUtil::Format(msg, __VA_ARGS__), __FUNCTION__)
class NotFoundException : public LTCException
{
public:
NotFoundException(const std::string& message, const std::string& function)
: LTCException("NotFoundException", message, function)
{
}
};

View File

@ -0,0 +1,74 @@
#pragma once
#include <mw/exceptions/LTCException.h>
#include <mw/util/StringUtil.h>
#define ThrowValidation(type) throw ValidationException(type, __FUNCTION__)
enum class EConsensusError
{
HASH_MISMATCH,
DUPLICATES,
BLOCK_WEIGHT,
BLOCK_SUMS,
STEALTH_SUMS,
INVALID_SIG,
BULLETPROOF,
PEGIN_MISMATCH,
PEGOUT_MISMATCH,
MMR_MISMATCH,
UTXO_MISSING,
UTXO_MISMATCH,
NOT_SORTED
};
class ValidationException : public LTCException
{
public:
ValidationException(const EConsensusError& type, const std::string& function)
: LTCException("ValidationException", GetMessage(type), function)
{
}
private:
static std::string GetMessage(const EConsensusError& type)
{
return StringUtil::Format("Consensus Error: {}", ToString(type));
}
static std::string ToString(const EConsensusError& type)
{
switch (type)
{
case EConsensusError::HASH_MISMATCH:
return "HASH_MISMATCH";
case EConsensusError::DUPLICATES:
return "DUPLICATES";
case EConsensusError::BLOCK_WEIGHT:
return "BLOCK_WEIGHT";
case EConsensusError::BLOCK_SUMS:
return "BLOCK_SUMS";
case EConsensusError::STEALTH_SUMS:
return "STEALTH_SUMS";
case EConsensusError::INVALID_SIG:
return "INVALID_SIG";
case EConsensusError::BULLETPROOF:
return "BULLETPROOF";
case EConsensusError::PEGIN_MISMATCH:
return "PEGIN_MISMATCH";
case EConsensusError::PEGOUT_MISMATCH:
return "PEGOUT_MISMATCH";
case EConsensusError::MMR_MISMATCH:
return "MMR_MISMATCH";
case EConsensusError::UTXO_MISSING:
return "UTXO_MISSING";
case EConsensusError::UTXO_MISMATCH:
return "UTXO_MISMATCH";
case EConsensusError::NOT_SORTED:
return "NOT_SORTED";
}
return "UNKNOWN";
}
};

View File

@ -0,0 +1,118 @@
#pragma once
#include <mw/file/File.h>
#include <mw/file/FilePath.h>
#include <mw/file/MemMap.h>
class AppendOnlyFile
{
public:
using Ptr = std::shared_ptr<AppendOnlyFile>;
AppendOnlyFile(const File& file, const size_t fileSize) noexcept
: m_file(file),
m_mmap(file.GetPath()),
m_fileSize(fileSize),
m_bufferIndex(fileSize)
{
}
virtual ~AppendOnlyFile() = default;
static AppendOnlyFile::Ptr Load(const FilePath& path)
{
File file(path);
file.Create();
auto pAppendOnlyFile = std::make_shared<AppendOnlyFile>(file, file.GetSize());
pAppendOnlyFile->m_mmap.Map();
return pAppendOnlyFile;
}
void Commit(const FilePath& new_path)
{
if (m_fileSize < m_bufferIndex) {
ThrowFile_F("Buffer index is past the end of {}", m_file);
}
m_mmap.Unmap();
m_file.CopyTo(new_path);
m_file = File(new_path);
if (m_fileSize != m_bufferIndex || !m_buffer.empty()) {
m_file.Write(m_bufferIndex, m_buffer, true);
m_fileSize = m_file.GetSize();
}
m_bufferIndex = m_fileSize;
m_buffer.clear();
m_mmap = MemMap{ m_file };
m_mmap.Map();
}
void Rollback() noexcept
{
m_bufferIndex = m_fileSize;
m_buffer.clear();
}
void Append(const std::vector<uint8_t>& data)
{
m_buffer.insert(m_buffer.end(), data.cbegin(), data.cend());
}
void Rewind(const uint64_t nextPosition)
{
assert(m_fileSize == m_bufferIndex);
if (nextPosition > (m_bufferIndex + m_buffer.size()))
{
ThrowFile_F("Tried to rewind past end of {}", m_file);
}
if (nextPosition <= m_bufferIndex)
{
m_buffer.clear();
m_bufferIndex = nextPosition;
}
else
{
m_buffer.erase(m_buffer.begin() + nextPosition - m_bufferIndex, m_buffer.end());
}
}
uint64_t GetSize() const noexcept
{
return m_bufferIndex + m_buffer.size();
}
std::vector<uint8_t> Read(const uint64_t position, const uint64_t numBytes) const
{
if ((position + numBytes) > (m_bufferIndex + m_buffer.size()))
{
ThrowFile_F("Tried to read past end of {}", m_file);
}
if (position < m_bufferIndex)
{
// FUTURE: Read from mapped and then from buffer, if necessary
return m_mmap.Read(position, numBytes);
}
else
{
auto begin = m_buffer.cbegin() + position - m_bufferIndex;
auto end = begin + numBytes;
return std::vector<uint8_t>(begin, end);
}
}
private:
File m_file;
MemMap m_mmap;
uint64_t m_fileSize;
uint64_t m_bufferIndex;
std::vector<uint8_t> m_buffer;
};

View File

@ -0,0 +1,37 @@
#pragma once
#include <mw/common/Traits.h>
#include <mw/file/FilePath.h>
#include <unordered_map>
class File : public Traits::IPrintable
{
public:
File(FilePath path) : m_path(std::move(path)) { }
virtual ~File() = default;
// Creates an empty file if it doesn't already exist
void Create();
const FilePath& GetPath() const noexcept { return m_path; }
size_t GetSize() const;
bool Exists() const;
std::vector<uint8_t> ReadBytes() const;
std::vector<uint8_t> ReadBytes(const size_t startIndex, const size_t numBytes) const;
void Write(const std::vector<uint8_t>& bytes);
void Write(const size_t startIndex, const std::vector<uint8_t>& bytes, const bool truncate);
void WriteBytes(const std::unordered_map<uint64_t, uint8_t>& bytes);
void Truncate(const uint64_t size);
void CopyTo(const FilePath& new_path) const;
//
// Traits
//
std::string Format() const final { return m_path.Format(); }
private:
FilePath m_path;
};

View File

@ -0,0 +1,133 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#if defined(__APPLE__)
#undef _GLIBCXX_HAVE_TIMESPEC_GET
#endif
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4100 4127 4244)
#endif
#include <boost/filesystem.hpp>
#include <ghc/filesystem.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
using error_code = std::error_code;
#include <mw/common/Traits.h>
#include <mw/exceptions/FileException.h>
#include <fstream>
class FilePath : public Traits::IPrintable
{
friend class File;
public:
//
// Constructors
//
FilePath(const FilePath& other) = default;
FilePath(FilePath&& other) = default;
FilePath(const boost::filesystem::path& path) : m_path(path.string()) {}
FilePath(const ghc::filesystem::path& path) : m_path(path) {}
FilePath(const char* path) : m_path(path) {}
FilePath(const std::string& u8str) : m_path(u8str) {}
//
// Destructor
//
virtual ~FilePath() = default;
//
// Operators
//
FilePath& operator=(const FilePath& other) = default;
FilePath& operator=(FilePath&& other) noexcept = default;
bool operator==(const FilePath& rhs) const noexcept { return m_path == rhs.m_path; }
FilePath GetChild(const ghc::filesystem::path& filename) const { return FilePath(m_path / filename); }
FilePath GetChild(const char* filename) const { return FilePath(m_path / ghc::filesystem::path(filename)); }
FilePath GetChild(const std::string& filename) const { return FilePath(m_path / ghc::filesystem::path(filename)); }
FilePath GetParent() const
{
if (!m_path.has_parent_path()) {
ThrowFile_F("Can't find parent path for {}", *this);
}
return FilePath(m_path.parent_path());
}
bool Exists() const
{
error_code ec;
const bool exists = ghc::filesystem::exists(m_path, ec);
if (ec) {
ThrowFile_F("Error ({}) while checking if {} exists", ec.message(), *this);
}
return exists;
}
bool Exists_Safe() const noexcept
{
error_code ec;
return ghc::filesystem::exists(m_path, ec);
}
bool IsDirectory() const
{
error_code ec;
const bool isDirectory = ghc::filesystem::is_directory(m_path, ec);
if (ec) {
ThrowFile_F("Error ({}) while checking if {} is a directory", ec.message(), *this);
}
return isDirectory;
}
bool IsDirectory_Safe() const noexcept
{
error_code ec;
return ghc::filesystem::is_directory(m_path, ec);
}
FilePath CreateDir() const
{
error_code ec;
ghc::filesystem::create_directories(m_path, ec);
if (ec && (!Exists_Safe() || !IsDirectory_Safe())) {
ThrowFile_F("Error ({}) while trying to create directory {}", ec.message(), *this);
}
return *this;
}
void Remove() const
{
error_code ec;
ghc::filesystem::remove_all(m_path, ec);
if (ec && Exists_Safe()) {
ThrowFile_F("Error ({}) while trying to remove {}", ec.message(), *this);
}
}
std::string ToString() const { return m_path.u8string(); }
boost::filesystem::path ToBoost() const { return boost::filesystem::path(m_path.u8string()); }
//
// Traits
//
std::string Format() const final { return m_path.u8string(); }
private:
ghc::filesystem::path m_path;
};

View File

@ -0,0 +1,79 @@
#pragma once
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4244)
#pragma warning(disable:4267)
#pragma warning(disable:4334)
#pragma warning(disable:4018)
#endif
#include <mio/mmap.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include <mw/file/File.h>
#include <cassert>
class MemMap
{
public:
MemMap(File file) noexcept
: m_file(std::move(file)), m_mapped(false) { }
void Map()
{
assert(!m_mapped);
if (m_file.GetSize() > 0) {
std::error_code error;
m_mmap = mio::make_mmap_source(m_file.GetPath().ToString(), error);
if (error) {
ThrowFile_F("Failed to mmap file: {}", error);
}
m_mapped = true;
}
}
void Unmap()
{
if (m_mapped) {
m_mmap.unmap();
m_mapped = false;
}
}
std::vector<uint8_t> Read(const size_t position, const size_t numBytes) const
{
assert(m_mapped);
return std::vector<uint8_t>(m_mmap.cbegin() + position, m_mmap.cbegin() + position + numBytes);
}
uint8_t ReadByte(const size_t position) const
{
assert(m_mapped);
return *((uint8_t*)(m_mmap.cbegin() + position));
}
bool empty() const noexcept
{
assert(m_mapped);
return m_mmap.empty();
}
size_t size() const noexcept
{
return m_mmap.size();
}
const File& GetFile() const noexcept { return m_file; }
File& GetFile() noexcept { return m_file; }
private:
File m_file;
mio::mmap_source m_mmap;
bool m_mapped;
};

View File

@ -0,0 +1,42 @@
#pragma once
// Copyright (c) 2018-2020 David Burkett
// Copyright (c) 2020 The Litecoin Developers
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Macros.h>
#include <mw/models/block/Block.h>
MW_NAMESPACE
/// <summary>
/// Interface for iterating sequentially blocks in the chain.
/// </summary>
class IChainIterator
{
public:
virtual ~IChainIterator() = default;
virtual void Next() noexcept = 0;
virtual bool Valid() const noexcept = 0;
virtual uint64_t GetHeight() const = 0;
virtual mw::Header::CPtr GetHeader() const = 0;
virtual mw::Block::CPtr GetBlock() const = 0;
};
/// <summary>
/// Interface for accessing blocks in the chain.
/// </summary>
class IChain
{
public:
using Ptr = std::shared_ptr<mw::IChain>;
virtual ~IChain() = default;
virtual std::unique_ptr<IChainIterator> NewIterator() = 0;
};
END_NAMESPACE // mw

View File

@ -0,0 +1,51 @@
#pragma once
// Copyright (c) 2018-2020 David Burkett
// Copyright (c) 2020 The Litecoin Developers
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Macros.h>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
MW_NAMESPACE
class DBBatch
{
public:
using UPtr = std::unique_ptr<DBBatch>;
virtual ~DBBatch() = default;
virtual void Write(const std::string& key, const std::vector<uint8_t>& value) = 0;
virtual void Erase(const std::string& key) = 0;
virtual void Commit() = 0;
};
class DBIterator
{
public:
virtual ~DBIterator() = default;
virtual void Seek(const std::string& key) = 0;
virtual void Next() = 0;
virtual bool GetKey(std::string& key) const = 0;
virtual bool Valid() const = 0;
};
class DBWrapper
{
public:
using Ptr = std::shared_ptr<DBWrapper>;
virtual ~DBWrapper() = default;
virtual bool Read(const std::string& key, std::vector<uint8_t>& value) const = 0;
virtual std::unique_ptr<DBIterator> NewIterator() = 0;
virtual std::unique_ptr<DBBatch> CreateBatch() = 0;
};
END_NAMESPACE // mw

View File

@ -0,0 +1,79 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Macros.h>
#include <cstdint>
#include <cassert>
#include <memory>
MMR_NAMESPACE
class Index
{
public:
Index() noexcept = default;
Index(const uint64_t position, const uint64_t height) noexcept
: m_position(position), m_height(height) { }
static Index At(const uint64_t position) noexcept;
bool operator==(const Index& rhs) const noexcept { return m_position == rhs.m_position && m_height == rhs.m_height; }
bool operator!=(const Index& rhs) const noexcept { return m_position != rhs.m_position || m_height != rhs.m_height; }
bool operator<(const Index& rhs) const noexcept { return m_position < rhs.m_position; }
bool operator<=(const Index& rhs) const noexcept { return m_position <= rhs.m_position; }
bool operator>(const Index& rhs) const noexcept { return m_position > rhs.m_position; }
bool operator>=(const Index& rhs) const noexcept { return m_position >= rhs.m_position; }
bool operator==(const uint64_t position) const noexcept { return m_position == position; }
bool operator!=(const uint64_t position) const noexcept { return m_position != position; }
bool operator<(const uint64_t position) const noexcept { return m_position < position; }
bool operator<=(const uint64_t position) const noexcept { return m_position <= position; }
bool operator>(const uint64_t position) const noexcept { return m_position > position; }
bool operator>=(const uint64_t position) const noexcept { return m_position >= position; }
mmr::Index& operator++() {
*this = this->GetNext();
return *this;
}
bool IsLeaf() const noexcept { return m_height == 0; }
uint64_t GetPosition() const noexcept { return m_position; }
uint64_t GetLeafIndex() const noexcept;
//
// Gets the height of the node (position).
// position is the zero-based postorder traversal index of a node in the tree.
//
// Height Index
//
// 2: 6 |
// / \ |
// / \ |
// 1: 2 5 |
// / \ / \ |
// 0: 0 1 3 4 |
//
uint64_t GetHeight() const noexcept { return m_height; }
Index GetNext() const noexcept { return Index::At(m_position + 1); }
Index GetParent() const noexcept;
Index GetSibling() const noexcept;
uint64_t left_child_pos() const noexcept { return GetLeftChild().GetPosition(); }
Index GetLeftChild() const noexcept;
Index GetRightChild() const noexcept;
uint64_t right_child_pos() const noexcept { return GetRightChild().GetPosition(); }
protected:
static uint64_t CalculateHeight(const uint64_t position) noexcept;
static uint64_t CalculateLeafIndex(const uint64_t position) noexcept;
uint64_t m_position;
uint64_t m_height;
};
END_NAMESPACE

View File

@ -0,0 +1,49 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Macros.h>
#include <mw/mmr/LeafIndex.h>
#include <mw/models/crypto/Hash.h>
#include <mw/crypto/Hasher.h>
MMR_NAMESPACE
class Leaf
{
public:
Leaf() noexcept = default;
Leaf(const LeafIndex& index, mw::Hash hash, std::vector<uint8_t> data)
: m_index(index), m_hash(std::move(hash)), m_data(std::move(data)) { }
static Leaf Create(const LeafIndex& index, std::vector<uint8_t> data)
{
mw::Hash hash = CalcHash(index, data);
return Leaf(index, std::move(hash), std::move(data));
}
static mw::Hash CalcHash(const LeafIndex& index, const std::vector<uint8_t>& data)
{
return Hasher()
.Append<uint64_t>(index.GetPosition())
.Append(data)
.hash();
}
bool operator!=(const Leaf& rhs) const noexcept { return m_hash != rhs.m_hash; }
bool operator==(const Leaf& rhs) const noexcept { return m_hash == rhs.m_hash; }
const Index& GetNodeIndex() const noexcept { return m_index.GetNodeIndex(); }
const LeafIndex& GetLeafIndex() const noexcept { return m_index; }
const mw::Hash& GetHash() const noexcept { return m_hash; }
const std::vector<uint8_t>& vec() const noexcept { return m_data; }
private:
LeafIndex m_index;
mw::Hash m_hash;
std::vector<uint8_t> m_data;
};
END_NAMESPACE

View File

@ -0,0 +1,67 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Macros.h>
#include <mw/common/Traits.h>
#include <mw/mmr/Index.h>
#include <mw/util/BitUtil.h>
MMR_NAMESPACE
class LeafIndex : public Traits::ISerializable
{
public:
LeafIndex() noexcept
: m_leafIndex(0), m_nodeIndex(0, 0) { }
LeafIndex(const uint64_t leafIndex, const uint64_t position)
: m_leafIndex(leafIndex), m_nodeIndex(position, 0) { }
virtual ~LeafIndex() = default;
static LeafIndex At(const uint64_t leafIndex) noexcept
{
return LeafIndex(leafIndex, (2 * leafIndex) - BitUtil::CountBitsSet(leafIndex));
}
bool operator<(const LeafIndex& rhs) const noexcept { return m_leafIndex < rhs.m_leafIndex; }
bool operator>(const LeafIndex& rhs) const noexcept { return m_leafIndex > rhs.m_leafIndex; }
bool operator==(const LeafIndex& rhs) const noexcept { return m_leafIndex == rhs.m_leafIndex; }
bool operator!=(const LeafIndex& rhs) const noexcept { return m_leafIndex != rhs.m_leafIndex; }
bool operator<=(const LeafIndex& rhs) const noexcept { return m_leafIndex <= rhs.m_leafIndex; }
bool operator>=(const LeafIndex& rhs) const noexcept { return m_leafIndex >= rhs.m_leafIndex; }
LeafIndex& operator++()
{
*this = this->Next();
return *this;
}
uint64_t Get() const noexcept { return m_leafIndex; }
const Index& GetNodeIndex() const noexcept { return m_nodeIndex; }
uint64_t GetPosition() const noexcept { return m_nodeIndex.GetPosition(); }
LeafIndex Next() const noexcept { return LeafIndex::At(m_leafIndex + 1); }
IMPL_SERIALIZED(LeafIndex);
template <typename Stream>
void Serialize(Stream& s) const
{
s << m_leafIndex;
}
template <typename Stream>
void Unserialize(Stream& s)
{
uint64_t leaf_idx;
s >> leaf_idx;
*this = LeafIndex::At(leaf_idx);
}
private:
uint64_t m_leafIndex;
Index m_nodeIndex;
};
END_NAMESPACE

View File

@ -0,0 +1,94 @@
#pragma once
#include <mw/common/Macros.h>
#include <mw/common/BitSet.h>
#include <mw/file/File.h>
#include <mw/file/MemMap.h>
#include <mw/models/crypto/Hash.h>
#include <mw/mmr/LeafIndex.h>
#include <unordered_map>
class ILeafSet
{
public:
using Ptr = std::shared_ptr<ILeafSet>;
virtual ~ILeafSet() = default;
virtual uint8_t GetByte(const uint64_t byteIdx) const = 0;
virtual void SetByte(const uint64_t byteIdx, const uint8_t value) = 0;
void Add(const mmr::LeafIndex& idx);
void Remove(const mmr::LeafIndex& idx);
bool Contains(const mmr::LeafIndex& idx) const noexcept;
mw::Hash Root() const;
void Rewind(const uint64_t numLeaves, const std::vector<mmr::LeafIndex>& leavesToAdd);
const mmr::LeafIndex& GetNextLeafIdx() const noexcept { return m_nextLeafIdx; }
BitSet ToBitSet() const;
virtual void ApplyUpdates(
const uint32_t file_index,
const mmr::LeafIndex& nextLeafIdx,
const std::unordered_map<uint64_t, uint8_t>& modifiedBytes
) = 0;
protected:
uint8_t BitToByte(const uint8_t bit) const;
ILeafSet(const mmr::LeafIndex& nextLeafIdx)
: m_nextLeafIdx(nextLeafIdx) { }
mmr::LeafIndex m_nextLeafIdx;
};
class LeafSet : public ILeafSet
{
public:
using Ptr = std::shared_ptr<LeafSet>;
static LeafSet::Ptr Open(const FilePath& leafset_dir, const uint32_t file_index);
static FilePath GetPath(const FilePath& leafset_dir, const uint32_t file_index);
uint8_t GetByte(const uint64_t byteIdx) const final;
void SetByte(const uint64_t byteIdx, const uint8_t value) final;
void ApplyUpdates(
const uint32_t file_index,
const mmr::LeafIndex& nextLeafIdx,
const std::unordered_map<uint64_t, uint8_t>& modifiedBytes
) final;
void Flush(const uint32_t file_index);
void Cleanup(const uint32_t current_file_index) const;
private:
LeafSet(FilePath dir, MemMap&& mmap, const mmr::LeafIndex& nextLeafIdx)
: ILeafSet(nextLeafIdx), m_dir(std::move(dir)), m_mmap(std::move(mmap)) {}
FilePath m_dir;
MemMap m_mmap;
std::unordered_map<uint64_t, uint8_t> m_modifiedBytes;
};
class LeafSetCache : public ILeafSet
{
public:
using Ptr = std::shared_ptr<LeafSetCache>;
using UPtr = std::unique_ptr<LeafSetCache>;
LeafSetCache(const ILeafSet::Ptr& pBacked)
: ILeafSet(pBacked->GetNextLeafIdx()), m_pBacked(pBacked) { }
uint8_t GetByte(const uint64_t byteIdx) const final;
void SetByte(const uint64_t byteIdx, const uint8_t value) final;
void ApplyUpdates(
const uint32_t file_index,
const mmr::LeafIndex& nextLeafIdx,
const std::unordered_map<uint64_t, uint8_t>& modifiedBytes
) final;
void Flush(const uint32_t file_index);
private:
ILeafSet::Ptr m_pBacked;
std::unordered_map<uint64_t, uint8_t> m_modifiedBytes;
};

View File

@ -0,0 +1,218 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Traits.h>
#include <mw/file/AppendOnlyFile.h>
#include <mw/file/FilePath.h>
#include <mw/models/crypto/Hash.h>
#include <mw/mmr/LeafIndex.h>
#include <mw/mmr/Leaf.h>
#include <mw/mmr/PruneList.h>
#include <mw/interfaces/db_interface.h>
/// <summary>
/// An interface for interacting with MMRs.
/// </summary>
class IMMR
{
public:
using Ptr = std::shared_ptr<IMMR>;
virtual ~IMMR() = default;
/// <summary>
/// Adds a new leaf with the given data to the end of the MMR.
/// </summary>
/// <param name="data">The serialized leaf data.</param>
/// <returns>The LeafIndex where the data was added.</returns>
virtual mmr::LeafIndex AddLeaf(const mmr::Leaf& leaf) = 0;
mmr::LeafIndex Add(const std::vector<uint8_t>& data) { return AddLeaf(mmr::Leaf::Create(GetNextLeafIdx(), data)); }
mmr::LeafIndex Add(const Traits::ISerializable& serializable) { return AddLeaf(mmr::Leaf::Create(GetNextLeafIdx(), serializable.Serialized())); }
/// <summary>
/// Retrieves the leaf at the given leaf index.
/// </summary>
/// <param name="leafIdx">The leaf index.</param>
/// <returns>The retrieved Leaf.</returns>
/// <throws>std::exception if index is beyond the end of the MMR.</throws>
/// <throws>std::exception if leaf at given index has been pruned.</throws>
virtual mmr::Leaf GetLeaf(const mmr::LeafIndex& leafIdx) const = 0;
/// <summary>
/// Retrieves the hash at the given MMR index.
/// </summary>
/// <param name="idx">The index, which may or may not be a leaf.</param>
/// <returns>The hash of the leaf or node at the index.</returns>
/// <throws>std::exception if index is beyond the end of the MMR.</throws>
/// <throws>std::exception if node at the given index has been pruned.</throws>
virtual mw::Hash GetHash(const mmr::Index& idx) const = 0;
/// <summary>
/// Retrieves the index of the next leaf to be added to the MMR.
/// eg. If the MMR contains 3 leaves (0, 1, 2), this will return LeafIndex 3.
/// </summary>
/// <returns>The next leaf index.</returns>
virtual mmr::LeafIndex GetNextLeafIdx() const noexcept = 0;
/// <summary>
/// Gets the number of leaves in the MMR, including those that have been pruned.
/// eg. If the MMR contains leaves 0, 1, and 2, this will return 3.
/// </summary>
/// <returns>The number of (pruned and unpruned) leaves in the MMR.</returns>
virtual uint64_t GetNumLeaves() const noexcept = 0;
/// <summary>
/// "Rewinds" the MMR to the given number of leaves.
/// In other words, it shrinks the MMR to the given size.
/// </summary>
/// <param name="numLeaves">The total number of (pruned and unpruned) leaves in the MMR.</param>
virtual void Rewind(const uint64_t numLeaves) = 0;
void Rewind(const mmr::LeafIndex& nextLeaf) { Rewind(nextLeaf.Get()); }
/// <summary>
/// Unlike a Merkle tree, an MMR generally has no single root so we need a method to compute one.
/// The process we use is called "bagging the peaks." We first identify the peaks (nodes with no parents).
/// We then "bag" them by hashing them iteratively from the right, using the total size of the MMR as prefix.
/// </summary>
/// <returns>The root hash of the MMR.</returns>
mw::Hash Root() const;
/// <summary>
/// Adds the given leaves to the MMR.
/// This also updates the database and MMR files when the MMR is not a cache.
/// Typically, this is called from a derived PMMRCache when its changes are being flushed/committed.
/// </summary>
/// <param name="file_index">The index of the MMR files. This should be incremented with each write.</param>
/// <param name="firstLeafIdx">The LeafIndex of the first leaf being added.</param>
/// <param name="leaves">The leaves being added to the MMR.</param>
/// <param name="pBatch">A wrapper around a DB Batch. Required when called for an MMR (ie, non-cache).</param>
virtual void BatchWrite(
const uint32_t file_index,
const mmr::LeafIndex& firstLeafIdx,
const std::vector<mmr::Leaf>& leaves,
const std::unique_ptr<mw::DBBatch>& pBatch
) = 0;
};
/// <summary>
/// Memory-only MMR with no pruning or file I/O.
/// </summary>
class MemMMR : public IMMR
{
public:
using Ptr = std::shared_ptr<MemMMR>;
MemMMR() = default;
virtual ~MemMMR() = default;
mmr::LeafIndex AddLeaf(const mmr::Leaf& leaf) final;
mmr::Leaf GetLeaf(const mmr::LeafIndex& leafIdx) const final;
mw::Hash GetHash(const mmr::Index& idx) const final;
mmr::LeafIndex GetNextLeafIdx() const noexcept final;
uint64_t GetNumLeaves() const noexcept final;
void Rewind(const uint64_t numLeaves) final;
void BatchWrite(
const uint32_t,
const mmr::LeafIndex&,
const std::vector<mmr::Leaf>&,
const std::unique_ptr<mw::DBBatch>&) final {}
private:
std::vector<mw::Hash> m_hashes;
std::vector<mmr::Leaf> m_leaves;
};
class PMMR : public IMMR
{
public:
using Ptr = std::shared_ptr<PMMR>;
static PMMR::Ptr Open(
const char dbPrefix,
const FilePath& mmr_dir,
const uint32_t file_index,
const mw::DBWrapper::Ptr& pDBWrapper,
const std::shared_ptr<const PruneList>& pPruneList
);
PMMR(const char dbPrefix,
const FilePath& mmr_dir,
const AppendOnlyFile::Ptr& pHashFile,
const std::shared_ptr<mw::DBWrapper>& pDBWrapper,
const PruneList::CPtr& pPruneList
) :
m_dbPrefix(dbPrefix),
m_dir(mmr_dir),
m_pHashFile(pHashFile),
m_pDatabase(pDBWrapper),
m_pPruneList(pPruneList) { }
virtual ~PMMR() = default;
static FilePath GetPath(const FilePath& dir, const char prefix, const uint32_t file_index);
mmr::LeafIndex AddLeaf(const mmr::Leaf& leaf) final;
mmr::Leaf GetLeaf(const mmr::LeafIndex& leafIdx) const final;
mw::Hash GetHash(const mmr::Index& idx) const final;
mmr::LeafIndex GetNextLeafIdx() const noexcept final { return mmr::LeafIndex::At(GetNumLeaves()); }
uint64_t GetNumLeaves() const noexcept final;
uint64_t GetNumNodes() const noexcept;
void Rewind(const uint64_t numLeaves) final;
void BatchWrite(
const uint32_t file_index,
const mmr::LeafIndex& firstLeafIdx,
const std::vector<mmr::Leaf>& leaves,
const std::unique_ptr<mw::DBBatch>& pBatch
) final;
void Cleanup(const uint32_t current_file_index) const;
private:
char m_dbPrefix;
FilePath m_dir;
AppendOnlyFile::Ptr m_pHashFile;
std::vector<mmr::Leaf> m_leaves;
std::map<mmr::LeafIndex, size_t> m_leafMap;
std::shared_ptr<mw::DBWrapper> m_pDatabase;
PruneList::CPtr m_pPruneList;
};
class PMMRCache : public IMMR
{
public:
using Ptr = std::shared_ptr<PMMRCache>;
PMMRCache(const IMMR::Ptr& pBacked)
: m_pBase(pBacked), m_firstLeaf(pBacked->GetNextLeafIdx()){ }
virtual ~PMMRCache() = default;
mmr::LeafIndex AddLeaf(const mmr::Leaf& leaf) final;
mmr::Leaf GetLeaf(const mmr::LeafIndex& leafIdx) const final;
mmr::LeafIndex GetNextLeafIdx() const noexcept final;
uint64_t GetNumLeaves() const noexcept final { return GetNextLeafIdx().Get(); }
mw::Hash GetHash(const mmr::Index& idx) const final;
void Rewind(const uint64_t numLeaves) final;
void BatchWrite(
const uint32_t file_index,
const mmr::LeafIndex& firstLeafIdx,
const std::vector<mmr::Leaf>& leaves,
const std::unique_ptr<mw::DBBatch>& pBatch
) final;
void Flush(const uint32_t index, const std::unique_ptr<mw::DBBatch>& pBatch);
private:
IMMR::Ptr m_pBase;
mmr::LeafIndex m_firstLeaf;
std::vector<mmr::Leaf> m_leaves;
std::vector<mw::Hash> m_nodes;
};

View File

@ -0,0 +1,37 @@
#pragma once
#include <mw/common/Traits.h>
#include <mw/models/crypto/Hash.h>
#include <boost/optional.hpp>
/// <summary>
/// Represents the state of the MMR files with the matching index.
/// </summary>
struct MMRInfo : public Traits::ISerializable
{
MMRInfo()
: version(0), index(0), pruned(mw::Hash()), compact_index(0), compacted(boost::none) { }
MMRInfo(uint8_t version, uint32_t index_in, mw::Hash pruned_in, uint32_t compact_index_in, boost::optional<mw::Hash> compacted_in)
: version(version), index(index_in), pruned(std::move(pruned_in)), compact_index(compact_index_in), compacted(std::move(compacted_in)) { }
// Version byte that allows for future modifications to the MMRInfo schema.
uint8_t version;
// File number of the PMMR files.
uint32_t index;
// Hash of latest header this PMMR represents.
mw::Hash pruned;
// File number of the PruneList bitset.
uint32_t compact_index;
// Hash of the header this PMMR was compacted for.
// You cannot rewind beyond this point.
boost::optional<mw::Hash> compacted;
IMPL_SERIALIZABLE(MMRInfo, obj)
{
READWRITE(obj.version, obj.index, obj.pruned, obj.compact_index, obj.compacted);
}
};

View File

@ -0,0 +1,44 @@
#pragma once
#include <mw/common/BitSet.h>
#include <mw/mmr/Index.h>
#include <mw/mmr/LeafIndex.h>
class MMRUtil
{
public:
static mw::Hash CalcParentHash(const mmr::Index& index, const mw::Hash& left_hash, const mw::Hash& right_hash);
static BitSet BuildCompactBitSet(const uint64_t num_leaves, const BitSet& unspent_leaf_indices);
static BitSet DiffCompactBitSet(const BitSet& prev_compact, const BitSet& new_compact);
/// <summary>
/// An MMR can be rebuilt from the leaves and a small set of carefully chosen parent hashes.
/// This calculates the positions of those parent hashes.
/// </summary>
/// <param name="unspent_leaf_indices">The unspent leaf indices.</param>
/// <returns>The pruned parent positions.</returns>
static BitSet CalcPrunedParents(const BitSet& unspent_leaf_indices);
};
/// <summary>
/// Iterates through MMR index positions at a single height, starting from left to right.
/// </summary>
struct SiblingIter
{
public:
SiblingIter(const uint64_t height, const mmr::Index& last_node);
bool Next();
const mmr::Index& Get() const noexcept { return m_next; }
uint64_t GetPosition() const noexcept { return m_next.GetPosition(); }
private:
uint64_t m_height;
mmr::Index m_lastNode;
uint64_t m_baseInc;
uint64_t m_siblingNum;
mmr::Index m_next;
};

View File

@ -0,0 +1,31 @@
#pragma once
#include <mw/file/FilePath.h>
#include <mw/mmr/Index.h>
#include <mw/mmr/LeafIndex.h>
#include <mw/common/BitSet.h>
#include <memory>
class PruneList
{
public:
using Ptr = std::shared_ptr<PruneList>;
using CPtr = std::shared_ptr<const PruneList>;
static PruneList::Ptr Open(const FilePath& parent_dir, const uint32_t file_index);
static FilePath GetPath(const FilePath& dir, const uint32_t file_index);
uint64_t GetShift(const mmr::Index& index) const noexcept;
uint64_t GetShift(const mmr::LeafIndex& index) const noexcept;
uint64_t GetTotalShift() const noexcept { return m_totalShift; }
void Commit(const uint32_t file_index, const BitSet& compacted);
private:
PruneList(const FilePath& dir, BitSet&& compacted, uint64_t total_shift)
: m_dir(dir), m_compacted(std::move(compacted)), m_totalShift(total_shift) { }
FilePath m_dir;
BitSet m_compacted;
uint64_t m_totalShift;
};

View File

@ -0,0 +1,129 @@
#pragma once
#include <mw/common/Macros.h>
#include <mw/common/Traits.h>
#include <mw/models/tx/TxBody.h>
#include <mw/models/block/Header.h>
#include <mw/models/tx/Kernel.h>
#include <mw/models/tx/PegInCoin.h>
#include <mw/models/tx/PegOutCoin.h>
#include <serialize.h>
#include <algorithm>
MW_NAMESPACE
class Block final :
public Traits::IPrintable,
public Traits::ISerializable,
public Traits::IHashable
{
public:
using Ptr = std::shared_ptr<Block>;
using CPtr = std::shared_ptr<const Block>;
//
// Constructors
//
Block(const mw::Header::CPtr& pHeader, TxBody body)
: m_pHeader(pHeader), m_body(std::move(body)) { }
Block(const Block& other) = default;
Block(Block&& other) noexcept = default;
Block() = default;
//
// Operators
//
Block& operator=(const Block& other) = default;
Block& operator=(Block&& other) noexcept = default;
//
// Getters
//
const mw::Header::CPtr& GetHeader() const noexcept { return m_pHeader; }
const TxBody& GetTxBody() const noexcept { return m_body; }
const std::vector<Input>& GetInputs() const noexcept { return m_body.GetInputs(); }
const std::vector<Output>& GetOutputs() const noexcept { return m_body.GetOutputs(); }
const std::vector<Kernel>& GetKernels() const noexcept { return m_body.GetKernels(); }
int32_t GetHeight() const noexcept { return m_pHeader->GetHeight(); }
const BlindingFactor& GetKernelOffset() const noexcept { return m_pHeader->GetKernelOffset(); }
const BlindingFactor& GetStealthOffset() const noexcept { return m_pHeader->GetStealthOffset(); }
CAmount GetTotalFee() const noexcept { return m_body.GetTotalFee(); }
std::vector<PegInCoin> GetPegIns() const noexcept { return m_body.GetPegIns(); }
CAmount GetPegInAmount() const noexcept { return m_body.GetPegInAmount(); }
std::vector<PegOutCoin> GetPegOuts() const noexcept { return m_body.GetPegOuts(); }
CAmount GetSupplyChange() const noexcept { return m_body.GetSupplyChange(); }
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(Block, obj)
{
READWRITE(obj.m_pHeader, obj.m_body);
}
//
// Traits
//
const mw::Hash& GetHash() const noexcept final { return m_pHeader->GetHash(); }
std::string Format() const final { return "Block(" + GetHash().ToHex() + ")"; }
//
// Context-free validation of the block.
//
void Validate() const;
private:
mw::Header::CPtr m_pHeader;
TxBody m_body;
};
class MutBlock
{
public:
//
// Constructors
//
MutBlock() = default;
MutBlock(const Block::CPtr& pBlock)
: m_header(pBlock->GetHeader()),
m_inputs(pBlock->GetInputs()),
m_outputs(pBlock->GetOutputs()),
m_kernels(pBlock->GetKernels()) {}
MutBlock& SetInputs(std::vector<Input> inputs) noexcept
{
m_inputs = std::move(inputs);
return *this;
}
MutBlock& SetOutputs(std::vector<Output> outputs) noexcept
{
m_outputs = std::move(outputs);
return *this;
}
MutBlock& SetKernels(std::vector<Kernel> kernels) noexcept
{
m_kernels = std::move(kernels);
return *this;
}
mw::Block::CPtr Build() const
{
return std::make_shared<mw::Block>(
m_header.Build(),
TxBody(m_inputs, m_outputs, m_kernels)
);
}
private:
MutHeader m_header;
std::vector<Input> m_inputs;
std::vector<Output> m_outputs;
std::vector<Kernel> m_kernels;
};
END_NAMESPACE

View File

@ -0,0 +1,51 @@
#pragma once
#include <mw/common/Macros.h>
#include <mw/common/Traits.h>
#include <mw/models/block/Header.h>
#include <mw/models/tx/UTXO.h>
MW_NAMESPACE
class BlockUndo : public Traits::ISerializable
{
public:
using CPtr = std::shared_ptr<const BlockUndo>;
//
// Constructors
//
BlockUndo(const mw::Header::CPtr& pPrevHeader, std::vector<UTXO>&& coinsSpent, std::vector<mw::Hash>&& coinsAdded)
: m_pPrevHeader(pPrevHeader), m_coinsSpent(std::move(coinsSpent)), m_coinsAdded(std::move(coinsAdded)) { }
BlockUndo(const BlockUndo& other) = default;
BlockUndo(BlockUndo&& other) noexcept = default;
BlockUndo() = default;
//
// Operators
//
BlockUndo& operator=(const BlockUndo& other) = default;
BlockUndo& operator=(BlockUndo&& other) noexcept = default;
//
// Getters
//
const mw::Header::CPtr& GetPreviousHeader() const noexcept { return m_pPrevHeader; }
const std::vector<UTXO>& GetCoinsSpent() const noexcept { return m_coinsSpent; }
const std::vector<mw::Hash>& GetCoinsAdded() const noexcept { return m_coinsAdded; }
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(BlockUndo, obj)
{
READWRITE(WrapOptionalPtr(obj.m_pPrevHeader), obj.m_coinsSpent, obj.m_coinsAdded);
}
private:
Header::CPtr m_pPrevHeader;
std::vector<UTXO> m_coinsSpent;
std::vector<mw::Hash> m_coinsAdded;
};
END_NAMESPACE

View File

@ -0,0 +1,196 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Macros.h>
#include <mw/common/Traits.h>
#include <mw/crypto/Hasher.h>
#include <mw/models/crypto/BlindingFactor.h>
#include <mw/models/crypto/Hash.h>
#include <cstdint>
#include <memory>
MW_NAMESPACE
class Header final :
public Traits::IPrintable,
public Traits::ISerializable,
public Traits::IHashable
{
public:
using CPtr = std::shared_ptr<const Header>;
//
// Constructors
//
Header() = default;
Header(
const int32_t height,
mw::Hash outputRoot,
mw::Hash kernelRoot,
mw::Hash leafsetRoot,
BlindingFactor kernelOffset,
BlindingFactor stealthOffset,
const uint64_t outputMMRSize,
const uint64_t kernelMMRSize
)
: m_height(height),
m_outputRoot(std::move(outputRoot)),
m_kernelRoot(std::move(kernelRoot)),
m_leafsetRoot(std::move(leafsetRoot)),
m_kernelOffset(std::move(kernelOffset)),
m_stealthOffset(std::move(stealthOffset)),
m_outputMMRSize(outputMMRSize),
m_kernelMMRSize(kernelMMRSize)
{
m_hash = Hashed(*this);
}
//
// Operators
//
bool operator==(const Header& rhs) const noexcept { return this->GetHash() == rhs.GetHash(); }
bool operator!=(const Header& rhs) const noexcept { return this->GetHash() != rhs.GetHash(); }
//
// Getters
//
int32_t GetHeight() const noexcept { return m_height; }
const mw::Hash& GetOutputRoot() const noexcept { return m_outputRoot; }
const mw::Hash& GetKernelRoot() const noexcept { return m_kernelRoot; }
const mw::Hash& GetLeafsetRoot() const noexcept { return m_leafsetRoot; }
const BlindingFactor& GetKernelOffset() const noexcept { return m_kernelOffset; }
const BlindingFactor& GetStealthOffset() const noexcept { return m_stealthOffset; }
uint64_t GetNumTXOs() const noexcept { return m_outputMMRSize; }
uint64_t GetNumKernels() const noexcept { return m_kernelMMRSize; }
//
// Traits
//
const mw::Hash& GetHash() const noexcept final { return m_hash; }
std::string Format() const final { return GetHash().ToHex(); }
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(Header, obj)
{
READWRITE(VARINT_MODE(obj.m_height, VarIntMode::NONNEGATIVE_SIGNED));
READWRITE(obj.m_outputRoot);
READWRITE(obj.m_kernelRoot);
READWRITE(obj.m_leafsetRoot);
READWRITE(obj.m_kernelOffset);
READWRITE(obj.m_stealthOffset);
READWRITE(VARINT(obj.m_outputMMRSize));
READWRITE(VARINT(obj.m_kernelMMRSize));
SER_READ(obj, obj.m_hash = Hashed(obj));
}
private:
int32_t m_height;
mw::Hash m_outputRoot;
mw::Hash m_kernelRoot;
mw::Hash m_leafsetRoot;
BlindingFactor m_kernelOffset;
BlindingFactor m_stealthOffset;
uint64_t m_outputMMRSize;
uint64_t m_kernelMMRSize;
mw::Hash m_hash;
};
class MutHeader
{
public:
//
// Constructors
//
MutHeader() = default;
MutHeader(const Header::CPtr& pHeader)
: m_height(pHeader->GetHeight()),
m_outputRoot(pHeader->GetOutputRoot()),
m_kernelRoot(pHeader->GetKernelRoot()),
m_leafsetRoot(pHeader->GetLeafsetRoot()),
m_kernelOffset(pHeader->GetKernelOffset()),
m_stealthOffset(pHeader->GetStealthOffset()),
m_numOutputs(pHeader->GetNumTXOs()),
m_numKernels(pHeader->GetNumKernels()) { }
MutHeader& SetHeight(const int32_t height) noexcept
{
m_height = height;
return *this;
}
MutHeader& SetOutputRoot(const mw::Hash& output_root) noexcept
{
m_outputRoot = output_root;
return *this;
}
MutHeader& SetKernelRoot(const mw::Hash& kernel_root) noexcept
{
m_kernelRoot = kernel_root;
return *this;
}
MutHeader& SetLeafsetRoot(const mw::Hash& leafset_root) noexcept
{
m_leafsetRoot = leafset_root;
return *this;
}
MutHeader& SetKernelOffset(const mw::Hash& kernel_offset) noexcept
{
m_kernelOffset = kernel_offset;
return *this;
}
MutHeader& SetStealthOffset(const mw::Hash& stealth_offset) noexcept
{
m_stealthOffset = stealth_offset;
return *this;
}
MutHeader& SetNumOutputs(const uint64_t num_outputs) noexcept
{
m_numOutputs = num_outputs;
return *this;
}
MutHeader& SetNumKernels(const uint64_t num_kernels) noexcept
{
m_numKernels = num_kernels;
return *this;
}
Header::CPtr Build() const
{
return std::make_shared<mw::Header>(
m_height,
m_outputRoot,
m_kernelRoot,
m_leafsetRoot,
m_kernelOffset,
m_stealthOffset,
m_numOutputs,
m_numKernels
);
}
private:
int32_t m_height;
mw::Hash m_outputRoot;
mw::Hash m_kernelRoot;
mw::Hash m_leafsetRoot;
BlindingFactor m_kernelOffset;
BlindingFactor m_stealthOffset;
uint64_t m_numOutputs;
uint64_t m_numKernels;
};
END_NAMESPACE

View File

@ -0,0 +1,192 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Traits.h>
#include <util/strencodings.h>
#include <cassert>
#include <stdexcept>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <array>
#ifdef _MSC_VER
#pragma warning(disable: 4505)
#endif
template<size_t NUM_BYTES, class ALLOC = std::allocator<uint8_t>>
class BigInt :
public Traits::IPrintable,
public Traits::ISerializable
{
public:
//
// Constructors
//
BigInt() noexcept : m_bytes(NUM_BYTES) { }
BigInt(const std::vector<uint8_t, ALLOC>& bytes) : m_bytes(bytes) { assert(m_bytes.size() == NUM_BYTES); }
BigInt(std::vector<uint8_t, ALLOC>&& bytes) : m_bytes(std::move(bytes)) { assert(m_bytes.size() == NUM_BYTES); }
BigInt(const std::array<uint8_t, NUM_BYTES>& bytes) : m_bytes(bytes.cbegin(), bytes.cend()) { }
explicit BigInt(const uint8_t* arr) : m_bytes(arr, arr + NUM_BYTES) { }
BigInt(const BigInt& bigInteger) = default;
BigInt(BigInt&& bigInteger) noexcept = default;
//
// Destructor
//
virtual ~BigInt()
{
for (size_t i = 0; i < m_bytes.size(); i++)
{
m_bytes[i] = 0;
}
}
static size_t size() noexcept { return NUM_BYTES; }
const std::vector<uint8_t, ALLOC>& vec() const { return m_bytes; }
uint8_t* data() { return m_bytes.data(); }
const uint8_t* data() const { return m_bytes.data(); }
bool IsZero() const noexcept
{
assert(m_bytes.size() == NUM_BYTES);
for (uint8_t byte : m_bytes)
{
if (byte != 0) {
return false;
}
}
return true;
}
static BigInt<NUM_BYTES, ALLOC> ValueOf(const uint8_t value)
{
std::vector<uint8_t, ALLOC> bytes(NUM_BYTES);
bytes[NUM_BYTES - 1] = value;
return BigInt<NUM_BYTES, ALLOC>(std::move(bytes));
}
static BigInt<NUM_BYTES, ALLOC> FromHex(const std::string& hex)
{
assert(hex.length() == NUM_BYTES * 2);
std::vector<uint8_t> bytes = ParseHex(hex);
assert(bytes.size() == NUM_BYTES);
return BigInt<NUM_BYTES, ALLOC>(std::move(bytes));
}
static BigInt<NUM_BYTES, ALLOC> Max()
{
std::vector<uint8_t, ALLOC> bytes(NUM_BYTES);
for (size_t i = 0; i < NUM_BYTES; i++)
{
bytes[i] = 0xFF;
}
return BigInt<NUM_BYTES, ALLOC>(std::move(bytes));
}
std::array<uint8_t, NUM_BYTES> ToArray() const noexcept
{
std::array<uint8_t, NUM_BYTES> arr;
std::copy_n(m_bytes.begin(), NUM_BYTES, arr.begin());
return arr;
}
std::string ToHex() const noexcept { return HexStr(m_bytes); }
std::string Format() const noexcept final { return ToHex(); }
//
// Operators
//
BigInt& operator=(const BigInt& other) = default;
BigInt& operator=(BigInt&& other) noexcept = default;
BigInt operator^(const BigInt& rhs) const
{
BigInt<NUM_BYTES, ALLOC> result = *this;
for (size_t i = 0; i < NUM_BYTES; i++)
{
result[i] ^= rhs[i];
}
return result;
}
uint8_t& operator[] (const size_t x) { return m_bytes[x]; }
const uint8_t& operator[] (const size_t x) const { return m_bytes[x]; }
bool operator<(const BigInt& rhs) const noexcept
{
if (this == &rhs)
{
return false;
}
assert(m_bytes.size() == NUM_BYTES && rhs.m_bytes.size() == NUM_BYTES);
for (size_t i = 0; i < NUM_BYTES; i++)
{
if (m_bytes[i] != rhs.m_bytes[i])
{
return m_bytes[i] < rhs.m_bytes[i];
}
}
return false;
}
bool operator>(const BigInt& rhs) const
{
return rhs < *this;
}
bool operator==(const BigInt& rhs) const
{
return this == &rhs || this->m_bytes == rhs.m_bytes;
}
bool operator!=(const BigInt& rhs) const
{
return !(*this == rhs);
}
bool operator<=(const BigInt& rhs) const
{
return *this < rhs || *this == rhs;
}
bool operator>=(const BigInt& rhs) const
{
return *this > rhs || *this == rhs;
}
BigInt operator^=(const BigInt& rhs)
{
*this = *this ^ rhs;
return *this;
}
//
// Serialization/Deserialization
//
IMPL_SERIALIZED(BigInt);
template <typename Stream>
void Serialize(Stream& s) const
{
s.write((const char*)m_bytes.data(), NUM_BYTES);
}
template <typename Stream>
void Unserialize(Stream& s)
{
s.read((char*)m_bytes.data(), NUM_BYTES);
}
private:
std::vector<uint8_t, ALLOC> m_bytes;
};

View File

@ -0,0 +1,65 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Traits.h>
#include <mw/models/crypto/BigInteger.h>
#include <mw/models/crypto/SecretKey.h>
class BlindingFactor : public Traits::ISerializable
{
public:
//
// Constructors
//
BlindingFactor() = default;
BlindingFactor(BigInt<32>&& value) : m_value(std::move(value)) { }
BlindingFactor(const BigInt<32>& value) : m_value(value) { }
BlindingFactor(const std::array<uint8_t, 32>& value) : m_value(value) { }
BlindingFactor(const SecretKey& key) : m_value(key.GetBigInt()) { }
BlindingFactor(const uint8_t* data) : m_value(data) { }
BlindingFactor(const BlindingFactor& other) = default;
BlindingFactor(BlindingFactor&& other) noexcept = default;
static BlindingFactor Random()
{
return BlindingFactor(SecretKey::Random());
}
//
// Operators
//
BlindingFactor& operator=(const BlindingFactor& other) = default;
BlindingFactor& operator=(BlindingFactor&& other) noexcept = default;
bool operator<(const BlindingFactor& rhs) const noexcept { return m_value < rhs.GetBigInt(); }
bool operator!=(const BlindingFactor& rhs) const noexcept { return m_value != rhs.GetBigInt(); }
bool operator==(const BlindingFactor& rhs) const noexcept { return m_value == rhs.GetBigInt(); }
//
// Getters
//
const BigInt<32>& GetBigInt() const noexcept { return m_value; }
const std::vector<uint8_t>& vec() const noexcept { return m_value.vec(); }
std::array<uint8_t, 32> array() const noexcept { return m_value.ToArray(); }
const uint8_t* data() const noexcept { return m_value.data(); }
uint8_t* data() noexcept { return m_value.data(); }
size_t size() const noexcept { return m_value.size(); }
bool IsZero() const noexcept { return m_value.IsZero(); }
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(BlindingFactor, obj)
{
READWRITE(obj.m_value);
}
std::string ToHex() const { return m_value.ToHex(); }
static BlindingFactor FromHex(const std::string& hex) { return BlindingFactor(BigInt<32>::FromHex(hex)); }
private:
// The 32 byte blinding factor.
BigInt<32> m_value;
};

View File

@ -0,0 +1,113 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Traits.h>
#include <mw/models/crypto/BigInteger.h>
#include <boost/functional/hash.hpp>
#include <cassert>
// Forward Declarations
class BlindingFactor;
class Commitment :
public Traits::IPrintable,
public Traits::ISerializable
{
public:
enum { SIZE = 33 };
//
// Constructors
//
Commitment() = default;
Commitment(BigInt<SIZE>&& bytes) : m_bytes(std::move(bytes)) { }
Commitment(const BigInt<SIZE>& bytes) : m_bytes(bytes) { }
Commitment(const std::array<uint8_t, SIZE>& bytes) : m_bytes(bytes) { }
Commitment(const Commitment& other) = default;
Commitment(Commitment&& other) noexcept = default;
//
// Destructor
//
virtual ~Commitment() = default;
//
// Factories
//
static Commitment Random();
static Commitment Switch(const BlindingFactor& blind, const uint64_t value);
static Commitment Blinded(const BlindingFactor& blind, const uint64_t value);
static Commitment Transparent(const uint64_t value);
//
// Operators
//
Commitment& operator=(const Commitment& other) = default;
Commitment& operator=(Commitment&& other) noexcept = default;
bool operator<(const Commitment& rhs) const noexcept { return m_bytes < rhs.GetBigInt(); }
bool operator>(const Commitment& rhs) const noexcept { return m_bytes > rhs.GetBigInt(); }
bool operator!=(const Commitment& rhs) const noexcept { return m_bytes != rhs.GetBigInt(); }
bool operator==(const Commitment& rhs) const noexcept { return m_bytes == rhs.GetBigInt(); }
//
// Getters
//
const BigInt<SIZE>& GetBigInt() const noexcept { return m_bytes; }
const std::vector<uint8_t>& vec() const noexcept { return m_bytes.vec(); }
std::array<uint8_t, SIZE> array() const noexcept { return m_bytes.ToArray(); }
const uint8_t* data() const noexcept { return m_bytes.data(); }
uint8_t* data() noexcept { return m_bytes.data(); }
size_t size() const noexcept { return m_bytes.size(); }
bool IsZero() const noexcept { return m_bytes.IsZero(); }
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(Commitment, obj)
{
READWRITE(obj.m_bytes);
}
std::string ToHex() const { return m_bytes.ToHex(); }
static Commitment FromHex(const std::string& hex) { return Commitment(BigInt<SIZE>::FromHex(hex)); }
//
// Traits
//
std::string Format() const final { return m_bytes.Format(); }
private:
BigInt<SIZE> m_bytes;
};
namespace std
{
template<>
struct hash<Commitment>
{
size_t operator()(const Commitment& commitment) const
{
return boost::hash_value(commitment.vec());
}
};
} // namespace std
class Commitments
{
public:
template <class T, typename SFINAE = typename std::enable_if_t<std::is_base_of<Traits::ICommitted, T>::value>>
static std::vector<Commitment> From(const std::vector<T>& committed) noexcept
{
std::vector<Commitment> commitments;
std::transform(
committed.cbegin(), committed.cend(),
std::back_inserter(commitments),
[](const T& committed) { return committed.GetCommitment(); });
return commitments;
}
};

View File

@ -0,0 +1,44 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Macros.h>
#include <mw/common/Traits.h>
#include <mw/models/crypto/BigInteger.h>
#include <boost/functional/hash.hpp>
MW_NAMESPACE
using Hash = BigInt<32>;
END_NAMESPACE
namespace std
{
template<>
struct hash<mw::Hash>
{
size_t operator()(const mw::Hash& hash) const
{
return boost::hash_value(hash.vec());
}
};
}
class Hashes
{
public:
template <class T, typename SFINAE = typename std::enable_if_t<std::is_base_of<Traits::IHashable, T>::value>>
static std::vector<mw::Hash> From(const std::vector<T>& vec_hashable) noexcept
{
std::vector<mw::Hash> hashes;
std::transform(
vec_hashable.cbegin(), vec_hashable.cend(),
std::back_inserter(hashes),
[](const T& hashable) { return hashable.GetHash(); });
return hashes;
}
};

View File

@ -0,0 +1,23 @@
#pragma once
#include <mw/models/crypto/Commitment.h>
#include <mw/models/crypto/RangeProof.h>
#include <cassert>
struct ProofData
{
Commitment commitment;
RangeProof::CPtr pRangeProof;
std::vector<uint8_t> extraData;
inline bool operator==(const ProofData& rhs) const noexcept
{
assert(rhs.pRangeProof != nullptr);
return commitment == rhs.commitment
&& *pRangeProof == *rhs.pRangeProof
&& extraData == rhs.extraData;
}
inline bool operator!=(const ProofData& rhs) const noexcept { return !(rhs == *this); }
};

View File

@ -0,0 +1,20 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/models/crypto/BigInteger.h>
class ProofMessage
{
public:
ProofMessage() = default;
ProofMessage(BigInt<20> bytes) : m_bytes(std::move(bytes)) { }
const BigInt<20>& GetBytes() const { return m_bytes; }
const uint8_t* data() const { return m_bytes.data(); }
private:
BigInt<20> m_bytes;
};

View File

@ -0,0 +1,90 @@
#pragma once
#include <mw/common/Traits.h>
#include <mw/models/crypto/BigInteger.h>
#include <mw/models/crypto/SecretKey.h>
#include <boost/functional/hash.hpp>
class PublicKey :
public Traits::IPrintable,
public Traits::ISerializable
{
public:
//
// Constructors
//
PublicKey() = default;
PublicKey(BigInt<33>&& compressed) : m_compressed(std::move(compressed)) {}
PublicKey(const BigInt<33>& compressed) : m_compressed(compressed) {}
PublicKey(const uint8_t* compressed) : m_compressed(compressed) {}
PublicKey(const PublicKey&) = default;
PublicKey(PublicKey&&) noexcept = default;
//
// Destructor
//
virtual ~PublicKey() = default;
//
// Operators
//
PublicKey& operator=(const PublicKey& rhs) = default;
PublicKey& operator=(PublicKey&& other) noexcept = default;
bool operator==(const PublicKey& rhs) const { return m_compressed == rhs.m_compressed; }
bool operator!=(const PublicKey& rhs) const { return m_compressed != rhs.m_compressed; }
bool operator<=(const PublicKey& rhs) const { return m_compressed <= rhs.m_compressed; }
bool operator<(const PublicKey& rhs) const { return m_compressed < rhs.m_compressed; }
bool operator>=(const PublicKey& rhs) const { return m_compressed >= rhs.m_compressed; }
bool operator>(const PublicKey& rhs) const { return m_compressed > rhs.m_compressed; }
//
// Factory
//
static PublicKey From(const SecretKey& key);
static PublicKey From(const Commitment& commitment);
static PublicKey Random();
const BigInt<33>& GetBigInt() const { return m_compressed; }
std::array<uint8_t, 33> array() const { return m_compressed.ToArray(); }
const std::vector<uint8_t>& vec() const { return m_compressed.vec(); }
const uint8_t& operator[](const size_t x) const { return m_compressed[x]; }
const uint8_t* data() const { return m_compressed.data(); }
uint8_t* data() { return m_compressed.data(); }
size_t size() const { return m_compressed.size(); }
//
// Point Arithmetic
//
PublicKey Mul(const SecretKey& mul) const;
PublicKey Div(const SecretKey& div) const;
PublicKey Add(const SecretKey& add) const;
PublicKey Add(const PublicKey& add) const;
PublicKey Sub(const SecretKey& sub) const;
PublicKey Sub(const PublicKey& sub) const;
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(PublicKey, obj)
{
READWRITE(obj.m_compressed);
}
std::string ToHex() const { return m_compressed.ToHex(); }
std::string Format() const final { return m_compressed.ToHex(); }
private:
BigInt<33> m_compressed;
};
namespace std
{
template<>
struct hash<PublicKey>
{
size_t operator()(const PublicKey& pubkey) const
{
return boost::hash_value(pubkey.vec());
}
};
}

View File

@ -0,0 +1,83 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Traits.h>
#include <mw/crypto/Hasher.h>
#include <util/strencodings.h>
#include <cassert>
class RangeProof :
public Traits::IPrintable,
public Traits::ISerializable,
public Traits::IHashable
{
public:
using CPtr = std::shared_ptr<const RangeProof>;
enum { SIZE = 675 };
//
// Constructors
//
RangeProof() noexcept : m_bytes(SIZE) {}
RangeProof(std::vector<uint8_t>&& bytes) : m_bytes(std::move(bytes))
{
assert(m_bytes.size() == SIZE);
m_hash = Hashed(*this);
}
RangeProof(const RangeProof& other) = default;
RangeProof(RangeProof&& other) noexcept = default;
//
// Destructor
//
virtual ~RangeProof() = default;
//
// Operators
//
RangeProof& operator=(const RangeProof& other) = default;
RangeProof& operator=(RangeProof&& other) noexcept = default;
bool operator==(const RangeProof& other) const noexcept { return m_bytes == other.m_bytes; }
//
// Getters
//
const std::vector<uint8_t>& vec() const { return m_bytes; }
const uint8_t* data() const { return m_bytes.data(); }
size_t size() const { return m_bytes.size(); }
//
// Serialization/Deserialization
//
IMPL_SERIALIZED(RangeProof);
template <typename Stream>
void Serialize(Stream& s) const
{
s.write((const char*)m_bytes.data(), SIZE);
}
template <typename Stream>
void Unserialize(Stream& s)
{
s.read((char*)m_bytes.data(), SIZE);
m_hash = Hashed(*this);
}
//
// Traits
//
std::string Format() const final { return HexStr(m_bytes); }
const mw::Hash& GetHash() const noexcept final { return m_hash; }
private:
// The proof itself, at most 675 bytes long.
std::vector<uint8_t> m_bytes;
mw::Hash m_hash;
};

View File

@ -0,0 +1,35 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/models/crypto/SecretKey.h>
#include <mw/models/crypto/ProofMessage.h>
class RewoundProof
{
public:
RewoundProof(const uint64_t amount, std::unique_ptr<SecretKey>&& pBlindingFactor, ProofMessage&& proofMessage)
: m_amount(amount), m_pBlindingFactor(std::move(pBlindingFactor)), m_proofMessage(std::move(proofMessage))
{
}
uint64_t GetAmount() const noexcept { return m_amount; }
const std::unique_ptr<SecretKey>& GetBlindingFactor() const noexcept { return m_pBlindingFactor; }
const ProofMessage& GetProofMessage() const noexcept { return m_proofMessage; }
bool operator==(const RewoundProof& rhs) const noexcept
{
return
m_amount == rhs.m_amount &&
m_pBlindingFactor->GetBigInt() == rhs.m_pBlindingFactor->GetBigInt() &&
m_proofMessage.GetBytes() == rhs.m_proofMessage.GetBytes();
}
private:
uint64_t m_amount;
std::unique_ptr<SecretKey> m_pBlindingFactor;
ProofMessage m_proofMessage;
};

View File

@ -0,0 +1,74 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Traits.h>
#include <mw/models/crypto/BigInteger.h>
#include <random.h>
template<size_t NUM_BYTES>
class secret_key_t : public Traits::ISerializable
{
public:
//
// Constructor
//
secret_key_t() = default;
secret_key_t(BigInt<NUM_BYTES>&& value) : m_value(std::move(value)) { }
secret_key_t(std::vector<uint8_t>&& bytes) : m_value(BigInt<NUM_BYTES>(std::move(bytes))) { }
secret_key_t(const std::array<uint8_t, NUM_BYTES>& bytes) : m_value(BigInt<NUM_BYTES>(bytes)) {}
secret_key_t(std::array<uint8_t, NUM_BYTES>&& bytes) : m_value(BigInt<NUM_BYTES>(std::move(bytes))) { }
secret_key_t(const uint8_t* bytes) : m_value(BigInt<NUM_BYTES>(bytes)) { }
static secret_key_t Random()
{
secret_key_t<NUM_BYTES> key;
size_t index = 0;
while (index < NUM_BYTES) {
size_t num_bytes = std::min(NUM_BYTES - index, (size_t)32);
GetStrongRandBytes(key.data() + index, num_bytes);
index += num_bytes;
}
return key;
}
//
// Destructor
//
virtual ~secret_key_t() = default;
//
// Operators
//
bool operator==(const secret_key_t<NUM_BYTES>& rhs) const noexcept { return m_value == rhs.m_value; }
//
// Getters
//
const BigInt<NUM_BYTES>& GetBigInt() const { return m_value; }
bool IsNull() const noexcept { return m_value.IsZero(); }
const std::vector<uint8_t>& vec() const { return m_value.vec(); }
std::array<uint8_t, 32> array() const noexcept { return m_value.ToArray(); }
uint8_t* data() { return m_value.data(); }
const uint8_t* data() const { return m_value.data(); }
uint8_t& operator[] (const size_t x) { return m_value[x]; }
const uint8_t& operator[] (const size_t x) const { return m_value[x]; }
size_t size() const { return m_value.size(); }
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(secret_key_t<NUM_BYTES>, obj)
{
READWRITE(obj.m_value);
}
private:
BigInt<NUM_BYTES> m_value;
};
using SecretKey = secret_key_t<32>;
using SecretKey64 = secret_key_t<64>;

View File

@ -0,0 +1,80 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Traits.h>
#include <mw/models/crypto/BigInteger.h>
class Signature :
public Traits::IPrintable,
public Traits::ISerializable
{
public:
using UPtr = std::unique_ptr<const Signature>;
enum { SIZE = 64 };
//
// Constructors
//
Signature() = default;
Signature(const uint8_t* data) : m_bytes(data) { }
Signature(BigInt<SIZE>&& bytes) : m_bytes(std::move(bytes)) { }
Signature(const BigInt<SIZE>& bytes) : m_bytes(bytes) { }
Signature(const Signature& other) = default;
Signature(Signature&& other) noexcept = default;
//
// Destructor
//
virtual ~Signature() = default;
//
// Operators
//
Signature& operator=(const Signature& other) = default;
Signature& operator=(Signature&& other) noexcept = default;
bool operator<(const Signature& rhs) const { return m_bytes < rhs.GetBigInt(); }
bool operator!=(const Signature& rhs) const { return m_bytes != rhs.GetBigInt(); }
bool operator==(const Signature& rhs) const { return m_bytes == rhs.GetBigInt(); }
//
// Getters
//
const BigInt<SIZE>& GetBigInt() const { return m_bytes; }
const std::vector<uint8_t>& vec() const { return m_bytes.vec(); }
const uint8_t* data() const { return m_bytes.data(); }
uint8_t* data() { return m_bytes.data(); }
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(Signature, obj)
{
READWRITE(obj.m_bytes);
}
std::string ToHex() const { return m_bytes.ToHex(); }
static Signature FromHex(const std::string& hex) { return Signature(BigInt<SIZE>::FromHex(hex)); }
//
// Traits
//
std::string Format() const final { return m_bytes.Format(); }
private:
// The 64 byte Signature.
BigInt<SIZE> m_bytes;
};
class CompactSignature : public Signature
{
public:
using UPtr = std::unique_ptr<const CompactSignature>;
CompactSignature() = default;
CompactSignature(BigInt<SIZE>&& bytes) : Signature(std::move(bytes)) { }
virtual ~CompactSignature() = default;
};

View File

@ -0,0 +1,72 @@
#pragma once
#include <mw/common/Traits.h>
#include <mw/crypto/Hasher.h>
#include <mw/models/crypto/Hash.h>
#include <mw/models/crypto/PublicKey.h>
#include <mw/models/crypto/Signature.h>
#include <boost/functional/hash.hpp>
/// <summary>
/// Contains a hashed message, a signature of that message, and the public key it was signed for.
/// </summary>
class SignedMessage : public Traits::ISerializable, public Traits::IHashable
{
public:
SignedMessage() = default;
SignedMessage(const SignedMessage&) = default;
SignedMessage(SignedMessage&&) = default;
SignedMessage(mw::Hash msgHash, PublicKey publicKey, Signature signature)
: m_messageHash(std::move(msgHash)), m_publicKey(std::move(publicKey)), m_signature(std::move(signature))
{
m_hash = Hashed(*this);
}
//
// Operators
//
SignedMessage& operator=(const SignedMessage& other) = default;
SignedMessage& operator=(SignedMessage&& other) noexcept = default;
bool operator==(const SignedMessage& rhs) const noexcept {
return m_messageHash == rhs.m_messageHash
&& m_publicKey == rhs.m_publicKey
&& m_signature == rhs.m_signature;
}
bool operator!=(const SignedMessage& rhs) const noexcept { return !(*this == rhs); }
//
// Getters
//
const mw::Hash& GetMsgHash() const noexcept { return m_messageHash; }
const PublicKey& GetPublicKey() const noexcept { return m_publicKey; }
const Signature& GetSignature() const noexcept { return m_signature; }
IMPL_SERIALIZABLE(SignedMessage, obj)
{
READWRITE(obj.m_messageHash);
READWRITE(obj.m_publicKey);
READWRITE(obj.m_signature);
SER_READ(obj, obj.m_hash = Hashed(obj));
}
const mw::Hash& GetHash() const noexcept final { return m_hash; }
private:
mw::Hash m_messageHash;
PublicKey m_publicKey;
Signature m_signature;
mw::Hash m_hash;
};
namespace std
{
template<>
struct hash<SignedMessage>
{
size_t operator()(const SignedMessage& hash) const
{
return boost::hash_value(hash.Serialized());
}
};
}

View File

@ -0,0 +1,111 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Traits.h>
#include <mw/crypto/Hasher.h>
#include <mw/models/crypto/Commitment.h>
#include <mw/models/crypto/PublicKey.h>
#include <mw/models/crypto/Signature.h>
#include <mw/models/crypto/SignedMessage.h>
////////////////////////////////////////
// INPUT
////////////////////////////////////////
class Input :
public Traits::ICommitted,
public Traits::IHashable,
public Traits::ISerializable
{
public:
//
// Constructors
//
Input(mw::Hash output_id, Commitment commitment, PublicKey input_pubkey, PublicKey output_pubkey, Signature signature)
: m_outputID(std::move(output_id)),
m_commitment(std::move(commitment)),
m_inputPubKey(std::move(input_pubkey)),
m_outputPubKey(std::move(output_pubkey)),
m_signature(std::move(signature))
{
m_hash = Hashed(*this);
}
Input(const Input& input) = default;
Input(Input&& input) noexcept = default;
Input() = default;
//
// Factories
//
static Input Create(const mw::Hash& output_id, const Commitment& commitment, const SecretKey& input_key, const SecretKey& output_key);
//
// Destructor
//
virtual ~Input() = default;
//
// Operators
//
Input& operator=(const Input& input) = default;
Input& operator=(Input&& input) noexcept = default;
bool operator<(const Input& input) const noexcept { return m_hash < input.m_hash; }
bool operator==(const Input& input) const noexcept { return m_hash == input.m_hash; }
//
// Getters
//
const mw::Hash& GetOutputID() const noexcept { return m_outputID; }
const Commitment& GetCommitment() const noexcept final { return m_commitment; }
const PublicKey& GetInputPubKey() const noexcept { return m_inputPubKey; }
const PublicKey& GetOutputPubKey() const noexcept { return m_outputPubKey; }
const Signature& GetSignature() const noexcept { return m_signature; }
SignedMessage BuildSignedMsg() const noexcept;
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(Input, obj)
{
READWRITE(obj.m_outputID);
READWRITE(obj.m_commitment);
READWRITE(obj.m_inputPubKey);
READWRITE(obj.m_outputPubKey);
READWRITE(obj.m_signature);
SER_READ(obj, obj.m_hash = Hashed(obj));
}
//
// Traits
//
const mw::Hash& GetHash() const noexcept final { return m_hash; }
private:
// The ID of the output being spent.
mw::Hash m_outputID;
// The commit referencing the output being spent.
Commitment m_commitment;
// The input pubkey.
PublicKey m_inputPubKey;
// The public key of the output being spent.
PublicKey m_outputPubKey;
Signature m_signature;
mw::Hash m_hash;
};
// Sorts by output ID (hash)
static const struct
{
bool operator()(const Input& a, const Input& b) const
{
return a.GetOutputID() < b.GetOutputID();
}
} InputSort;

View File

@ -0,0 +1,228 @@
#pragma once
#include <mw/common/Macros.h>
#include <mw/common/Traits.h>
#include <mw/crypto/Hasher.h>
#include <mw/models/crypto/BlindingFactor.h>
#include <mw/models/crypto/Commitment.h>
#include <mw/models/crypto/SecretKey.h>
#include <mw/models/crypto/Signature.h>
#include <mw/models/crypto/SignedMessage.h>
#include <mw/models/tx/PegOutCoin.h>
#include <amount.h>
#include <boost/optional.hpp>
#include <numeric>
class Kernel :
public Traits::ICommitted,
public Traits::IHashable,
public Traits::ISerializable
{
public:
Kernel() = default;
Kernel(
const uint8_t features,
boost::optional<CAmount> fee,
boost::optional<CAmount> pegin,
std::vector<PegOutCoin> pegouts,
boost::optional<int32_t> lockHeight,
boost::optional<PublicKey> stealthExcess,
std::vector<uint8_t> extraData,
Commitment excess,
Signature signature
) : m_features(features),
m_fee(fee),
m_pegin(pegin),
m_pegouts(std::move(pegouts)),
m_lockHeight(lockHeight),
m_stealthExcess(std::move(stealthExcess)),
m_extraData(std::move(extraData)),
m_excess(std::move(excess)),
m_signature(std::move(signature))
{
m_hash = Hashed(*this);
}
enum FeatureBit {
FEE_FEATURE_BIT = 0x01,
PEGIN_FEATURE_BIT = 0x02,
PEGOUT_FEATURE_BIT = 0x04,
HEIGHT_LOCK_FEATURE_BIT = 0x08,
STEALTH_EXCESS_FEATURE_BIT = 0x10,
EXTRA_DATA_FEATURE_BIT = 0x20,
ALL_FEATURE_BITS = FEE_FEATURE_BIT | PEGIN_FEATURE_BIT | PEGOUT_FEATURE_BIT | HEIGHT_LOCK_FEATURE_BIT | STEALTH_EXCESS_FEATURE_BIT | EXTRA_DATA_FEATURE_BIT
};
//
// Factories
//
static Kernel Create(
const BlindingFactor& blind,
const boost::optional<SecretKey>& stealth_blind,
const boost::optional<CAmount>& fee,
const boost::optional<CAmount>& pegin_amount,
const std::vector<PegOutCoin>& pegouts,
const boost::optional<int32_t>& lock_height
);
//
// Operators
//
bool operator<(const Kernel& rhs) const { return GetKernelID() < rhs.GetKernelID(); }
bool operator==(const Kernel& rhs) const { return GetKernelID() == rhs.GetKernelID(); }
bool operator!=(const Kernel& rhs) const { return GetKernelID() != rhs.GetKernelID(); }
//
// Getters
//
const mw::Hash& GetKernelID() const noexcept { return m_hash; }
uint8_t GetFeatures() const noexcept { return m_features; }
CAmount GetFee() const noexcept { return m_fee.value_or(0); }
int32_t GetLockHeight() const noexcept { return m_lockHeight.value_or(0); }
const Commitment& GetExcess() const noexcept { return m_excess; }
const PublicKey& GetStealthExcess() const noexcept { return m_stealthExcess.value(); }
const Signature& GetSignature() const noexcept { return m_signature; }
const std::vector<uint8_t>& GetExtraData() const noexcept { return m_extraData; }
bool IsStandard() const noexcept { return m_features < EXTRA_DATA_FEATURE_BIT; }
SignedMessage BuildSignedMsg() const;
static mw::Hash GetSignatureMessage(
const uint8_t features,
const Commitment& excess_commitment,
const boost::optional<PublicKey>& stealth_commitment,
const boost::optional<CAmount>& fee,
const boost::optional<CAmount>& pegin_amount,
const std::vector<PegOutCoin>& pegouts,
const boost::optional<int32_t>& lock_height,
const std::vector<uint8_t>& extra_data
);
bool HasPegIn() const noexcept { return !!m_pegin; }
bool HasPegOut() const noexcept { return !m_pegouts.empty(); }
bool HasStealthExcess() const noexcept { return !!m_stealthExcess; }
CAmount GetPegIn() const noexcept { return m_pegin.value_or(0); }
const std::vector<PegOutCoin>& GetPegOuts() const noexcept { return m_pegouts; }
CAmount GetPegOutAmount() const noexcept
{
return std::accumulate(
m_pegouts.cbegin(), m_pegouts.cend(), (CAmount)0,
[](CAmount sum, const PegOutCoin& pegout) { return sum + pegout.GetAmount(); }
);
}
CAmount GetSupplyChange() const noexcept
{
return (m_pegin.value_or(0) - m_fee.value_or(0)) - GetPegOutAmount();
}
//
// Serialization/Deserialization
//
IMPL_SERIALIZED(Kernel);
template <typename Stream>
void Serialize(Stream& s) const
{
s << m_features;
if (m_fee) {
::WriteVarInt<Stream, VarIntMode::NONNEGATIVE_SIGNED, CAmount>(s, m_fee.value());
}
if (m_pegin) {
::WriteVarInt<Stream, VarIntMode::NONNEGATIVE_SIGNED, CAmount>(s, m_pegin.value());
}
if (!m_pegouts.empty()) {
s << m_pegouts;
}
if (m_lockHeight) {
::WriteVarInt<Stream, VarIntMode::NONNEGATIVE_SIGNED, int32_t>(s, m_lockHeight.value());
}
if (m_stealthExcess) {
s << m_stealthExcess.value();
}
if (!m_extraData.empty()) {
s << m_extraData;
}
s << m_excess << m_signature;
}
template <typename Stream>
void Unserialize(Stream& s)
{
s >> m_features;
if (m_features & FEE_FEATURE_BIT) {
m_fee = ::ReadVarInt<Stream, VarIntMode::NONNEGATIVE_SIGNED, CAmount>(s);
}
if (m_features & PEGIN_FEATURE_BIT) {
m_pegin = ::ReadVarInt<Stream, VarIntMode::NONNEGATIVE_SIGNED, CAmount>(s);
}
if (m_features & PEGOUT_FEATURE_BIT) {
s >>m_pegouts;
}
if (m_features & HEIGHT_LOCK_FEATURE_BIT) {
m_lockHeight = ::ReadVarInt<Stream, VarIntMode::NONNEGATIVE_SIGNED, int32_t>(s);
}
if (m_features & STEALTH_EXCESS_FEATURE_BIT) {
PublicKey stealth_excess;
s >> stealth_excess;
m_stealthExcess = boost::make_optional(std::move(stealth_excess));
}
if (m_features & EXTRA_DATA_FEATURE_BIT) {
s >> m_extraData;
}
s >> m_excess >> m_signature;
m_hash = Hashed(*this);
}
//
// Traits
//
const mw::Hash& GetHash() const noexcept final { return m_hash; }
const Commitment& GetCommitment() const noexcept final { return m_excess; }
private:
uint8_t m_features;
boost::optional<CAmount> m_fee;
boost::optional<CAmount> m_pegin;
std::vector<PegOutCoin> m_pegouts;
boost::optional<int32_t> m_lockHeight;
boost::optional<PublicKey> m_stealthExcess;
std::vector<uint8_t> m_extraData;
// Remainder of the sum of all transaction commitments.
// If the transaction is well formed, amounts components should sum to zero and the excess is hence a valid public key.
Commitment m_excess;
// The signature proving the excess is a valid public key, which signs the transaction fee.
Signature m_signature;
mw::Hash m_hash;
};
// Sorts by net supply increase [pegin - (fee + pegout)] with highest increase first, then sorts by hash.
static const struct
{
bool operator()(const Kernel& a, const Kernel& b) const
{
CAmount a_pegin = a.GetSupplyChange();
CAmount b_pegin = b.GetSupplyChange();
return (a_pegin > b_pegin) || (a_pegin == b_pegin && a.GetHash() < b.GetHash());
}
} KernelSort;

View File

@ -0,0 +1,231 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Traits.h>
#include <mw/models/crypto/BlindingFactor.h>
#include <mw/models/crypto/Commitment.h>
#include <mw/models/crypto/ProofData.h>
#include <mw/models/crypto/RangeProof.h>
#include <mw/models/crypto/SecretKey.h>
#include <mw/models/crypto/SignedMessage.h>
// Forward Declarations
class StealthAddress;
////////////////////////////////////////
// OUTPUT MESSAGE
////////////////////////////////////////
class OutputMessage : public Traits::ISerializable, public Traits::IHashable
{
public:
//
// Constructors
//
OutputMessage(
uint8_t features_,
PublicKey keyExchangePubKey_,
uint8_t viewTag_,
uint64_t maskedValue_,
BigInt<16> maskedNonce_
) :
features(features_),
key_exchange_pubkey(std::move(keyExchangePubKey_)),
view_tag(viewTag_),
masked_value(maskedValue_),
masked_nonce(std::move(maskedNonce_))
{
m_hash = Hashed(*this);
}
OutputMessage(const OutputMessage& output_message) = default;
OutputMessage(OutputMessage&& output_message) noexcept = default;
OutputMessage() = default;
enum FeatureBit {
STANDARD_FIELDS_FEATURE_BIT = 0x01,
EXTRA_DATA_FEATURE_BIT = 0x02
};
//
// Operators
//
OutputMessage& operator=(const OutputMessage& output_message) = default;
OutputMessage& operator=(OutputMessage&& output_message) noexcept = default;
bool operator<(const OutputMessage& output_message) const noexcept { return m_hash < output_message.m_hash; }
bool operator==(const OutputMessage& output_message) const noexcept { return m_hash == output_message.m_hash; }
uint8_t features;
PublicKey key_exchange_pubkey;
uint8_t view_tag;
uint64_t masked_value;
BigInt<16> masked_nonce;
std::vector<uint8_t> extra_data;
IMPL_SERIALIZABLE(OutputMessage, obj)
{
READWRITE(obj.features);
if (obj.features & STANDARD_FIELDS_FEATURE_BIT) {
READWRITE(obj.key_exchange_pubkey);
READWRITE(obj.view_tag);
READWRITE(obj.masked_value);
READWRITE(obj.masked_nonce);
}
if (obj.features & EXTRA_DATA_FEATURE_BIT) {
READWRITE(obj.extra_data);
}
SER_READ(obj, obj.m_hash = Hashed(obj));
}
//
// Traits
//
const mw::Hash& GetHash() const noexcept final { return m_hash; }
private:
mw::Hash m_hash;
};
////////////////////////////////////////
// OUTPUT
////////////////////////////////////////
class Output :
public Traits::ICommitted,
public Traits::ISerializable,
public Traits::IHashable
{
public:
//
// Constructors
//
Output(
Commitment commitment,
PublicKey senderPubKey,
PublicKey receiverPubKey,
OutputMessage message,
const RangeProof::CPtr& pProof,
Signature signature
) :
m_commitment(std::move(commitment)),
m_senderPubKey(std::move(senderPubKey)),
m_receiverPubKey(std::move(receiverPubKey)),
m_message(std::move(message)),
m_pProof(pProof),
m_signature(std::move(signature))
{
m_hash = ComputeHash();
}
Output(const Output& Output) = default;
Output(Output&& Output) noexcept = default;
Output() = default;
//
// Factory
//
static Output Create(
BlindingFactor* blind_out,
const SecretKey& sender_privkey,
const StealthAddress& receiver_addr,
const uint64_t value
);
//
// Destructor
//
virtual ~Output() = default;
//
// Operators
//
Output& operator=(const Output& output) = default;
Output& operator=(Output&& output) noexcept = default;
bool operator<(const Output& output) const noexcept { return m_hash < output.m_hash; }
bool operator==(const Output& output) const noexcept { return m_hash == output.m_hash; }
//
// Getters
//
const mw::Hash& GetOutputID() const noexcept { return m_hash; }
const Commitment& GetCommitment() const noexcept final { return m_commitment; }
const PublicKey& GetSenderPubKey() const noexcept { return m_senderPubKey; }
const PublicKey& GetReceiverPubKey() const noexcept { return m_receiverPubKey; }
const RangeProof::CPtr& GetRangeProof() const noexcept { return m_pProof; }
const OutputMessage& GetOutputMessage() const noexcept { return m_message; }
const Signature& GetSignature() const noexcept { return m_signature; }
bool IsStandard() const noexcept { return m_message.features == OutputMessage::STANDARD_FIELDS_FEATURE_BIT; }
const std::vector<uint8_t>& GetExtraData() const noexcept { return m_message.extra_data; }
uint8_t GetFeatures() const noexcept { return m_message.features; }
bool HasStandardFields() const noexcept { return m_message.features & OutputMessage::STANDARD_FIELDS_FEATURE_BIT; }
const PublicKey& GetKeyExchangePubKey() const noexcept { return m_message.key_exchange_pubkey; }
uint8_t GetViewTag() const noexcept { return m_message.view_tag; }
uint64_t GetMaskedValue() const noexcept { return m_message.masked_value; }
const BigInt<16>& GetMaskedNonce() const noexcept { return m_message.masked_nonce; }
const PublicKey& Ko() const noexcept { return m_receiverPubKey; }
const PublicKey& Ke() const noexcept { return m_message.key_exchange_pubkey; }
SignedMessage BuildSignedMsg() const noexcept;
ProofData BuildProofData() const noexcept;
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(Output, obj)
{
READWRITE(obj.m_commitment);
READWRITE(obj.m_senderPubKey);
READWRITE(obj.m_receiverPubKey);
READWRITE(obj.m_message);
READWRITE(obj.m_pProof);
READWRITE(obj.m_signature);
SER_READ(obj, obj.m_hash = obj.ComputeHash());
}
//
// Traits
//
const mw::Hash& GetHash() const noexcept final { return m_hash; }
private:
//
// Outputs use a special serialization when hashing that only includes
// the hash of the rangeproof, instead of the full 675 byte rangeproof.
//
// This will make some light client use cases more efficient.
//
mw::Hash ComputeHash() const noexcept
{
return Hasher()
.Append(m_commitment)
.Append(m_senderPubKey)
.Append(m_receiverPubKey)
.Append(m_message.GetHash())
.Append(m_pProof->GetHash())
.Append(m_signature)
.hash();
}
Commitment m_commitment;
PublicKey m_senderPubKey;
PublicKey m_receiverPubKey;
OutputMessage m_message;
RangeProof::CPtr m_pProof;
Signature m_signature;
mw::Hash m_hash;
};
// Sorts by output ID (hash)
static const struct
{
bool operator()(const Output& a, const Output& b) const
{
return a.GetOutputID() < b.GetOutputID();
}
} OutputSort;

View File

@ -0,0 +1,41 @@
#pragma once
#include <mw/crypto/Hasher.h>
#include <mw/crypto/Pedersen.h>
#include <mw/models/crypto/BlindingFactor.h>
#include <mw/models/crypto/SecretKey.h>
class OutputMask
{
public:
OutputMask(const OutputMask&) = default;
OutputMask(OutputMask&&) noexcept = default;
//
// Feeds the shared secret 't' into tagged hash functions to derive:
// q - the blinding factor
// v' - the value mask
// n' - the nonce mask
//
static OutputMask FromShared(const SecretKey& shared_secret)
{
OutputMask mask;
mask.pre_blind = Hashed(EHashTag::BLIND, shared_secret);
mask.value_mask = *((uint64_t*)Hashed(EHashTag::VALUE_MASK, shared_secret).data());
mask.nonce_mask = BigInt<16>(Hashed(EHashTag::NONCE_MASK, shared_secret).data());
return mask;
}
const BlindingFactor& GetRawBlind() const noexcept { return pre_blind; }
BlindingFactor BlindSwitch(const uint64_t value) const { return Pedersen::BlindSwitch(pre_blind, value); }
Commitment SwitchCommit(const uint64_t value) const { return Commitment::Switch(pre_blind, value); }
BigInt<16> MaskNonce(const BigInt<16>& nonce) const { return nonce ^ nonce_mask; }
uint64_t MaskValue(const uint64_t value) const { return value ^ value_mask; }
private:
OutputMask() = default;
BlindingFactor pre_blind;
uint64_t value_mask;
BigInt<16> nonce_mask;
};

View File

@ -0,0 +1,43 @@
#pragma once
#include <mw/common/Traits.h>
#include <mw/models/crypto/Hash.h>
#include <mw/util/StringUtil.h>
#include <amount.h>
//
// Represents coins being pegged in, i.e. moved from canonical chain to the extension block.
//
class PegInCoin : public Traits::ISerializable, public Traits::IPrintable
{
public:
PegInCoin() = default;
PegInCoin(const CAmount amount, mw::Hash kernel_id)
: m_amount(amount), m_kernelID(std::move(kernel_id)) {}
bool operator==(const PegInCoin& rhs) const noexcept
{
return m_amount == rhs.m_amount && m_kernelID == rhs.m_kernelID;
}
CAmount GetAmount() const noexcept { return m_amount; }
const mw::Hash& GetKernelID() const noexcept { return m_kernelID; }
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(PegInCoin, obj)
{
READWRITE(VARINT_MODE(obj.m_amount, VarIntMode::NONNEGATIVE_SIGNED));
READWRITE(obj.m_kernelID);
}
std::string Format() const noexcept final
{
return StringUtil::Format("PegInCoin(kernel_id: {}, amount: {})", m_kernelID, m_amount);
}
private:
CAmount m_amount;
mw::Hash m_kernelID;
};

View File

@ -0,0 +1,64 @@
#pragma once
#include <mw/common/Traits.h>
#include <mw/util/StringUtil.h>
#include <script/script.h>
#include <util/strencodings.h>
#include <amount.h>
//
// Represents coins being pegged out, i.e. moved from the extension block to the canonical chain.
//
class PegOutCoin : public Traits::ISerializable, public Traits::IPrintable
{
public:
PegOutCoin() = default;
PegOutCoin(const CAmount amount, CScript scriptPubKey)
: m_amount(amount), m_scriptPubKey(std::move(scriptPubKey)) { }
bool operator<(const PegOutCoin& rhs) const noexcept
{
if (m_amount != rhs.m_amount) {
return m_amount < rhs.m_amount;
}
return m_scriptPubKey < rhs.m_scriptPubKey;
}
bool operator!=(const PegOutCoin& rhs) const noexcept
{
return m_amount != rhs.m_amount || m_scriptPubKey != rhs.m_scriptPubKey;
}
bool operator==(const PegOutCoin& rhs) const noexcept
{
return m_amount == rhs.m_amount && m_scriptPubKey == rhs.m_scriptPubKey;
}
CAmount GetAmount() const noexcept { return m_amount; }
const CScript& GetScriptPubKey() const noexcept { return m_scriptPubKey; }
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(PegOutCoin, obj)
{
READWRITE(VARINT_MODE(obj.m_amount, VarIntMode::NONNEGATIVE_SIGNED));
READWRITE(obj.m_scriptPubKey);
if (ser_action.ForRead()) {
if (obj.m_scriptPubKey.empty()) {
throw std::ios_base::failure("Pegout scriptPubKey must not be empty");
}
}
}
std::string Format() const noexcept final
{
return StringUtil::Format("PegOutCoin(scriptPubKey:{}, amount:{})", HexStr(m_scriptPubKey), m_amount);
}
private:
CAmount m_amount;
CScript m_scriptPubKey;
};

View File

@ -0,0 +1,157 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Macros.h>
#include <mw/common/Traits.h>
#include <mw/consensus/Weight.h>
#include <mw/models/crypto/Hash.h>
#include <mw/models/crypto/BigInteger.h>
#include <mw/models/crypto/BlindingFactor.h>
#include <mw/models/tx/TxBody.h>
#include <memory>
#include <numeric>
#include <vector>
MW_NAMESPACE
////////////////////////////////////////
// TRANSACTION - Represents a transaction or merged transactions before they've been included in a block.
////////////////////////////////////////
class Transaction :
public Traits::IPrintable,
public Traits::ISerializable,
public Traits::IHashable
{
public:
using CPtr = std::shared_ptr<const Transaction>;
//
// Constructors
//
Transaction(BlindingFactor kernel_offset, BlindingFactor stealth_offset, TxBody body)
: m_kernelOffset(std::move(kernel_offset)), m_stealthOffset(std::move(stealth_offset)), m_body(std::move(body))
{
m_hash = Hashed(*this);
}
Transaction(const Transaction& transaction) = default;
Transaction(Transaction&& transaction) noexcept = default;
Transaction() = default;
//
// Factory
//
static mw::Transaction::CPtr Create(
BlindingFactor kernel_offset,
BlindingFactor owner_offset,
std::vector<Input> inputs,
std::vector<Output> outputs,
std::vector<Kernel> kernels
);
//
// Destructor
//
virtual ~Transaction() = default;
//
// Operators
//
Transaction& operator=(const Transaction& transaction) = default;
Transaction& operator=(Transaction&& transaction) noexcept = default;
bool operator<(const Transaction& transaction) const noexcept { return GetHash() < transaction.GetHash(); }
bool operator==(const Transaction& transaction) const noexcept { return GetHash() == transaction.GetHash(); }
bool operator!=(const Transaction& transaction) const noexcept { return GetHash() != transaction.GetHash(); }
//
// Getters
//
const BlindingFactor& GetKernelOffset() const noexcept { return m_kernelOffset; }
const BlindingFactor& GetStealthOffset() const noexcept { return m_stealthOffset; }
const TxBody& GetBody() const noexcept { return m_body; }
const std::vector<Input>& GetInputs() const noexcept { return m_body.GetInputs(); }
const std::vector<Output>& GetOutputs() const noexcept { return m_body.GetOutputs(); }
const std::vector<Kernel>& GetKernels() const noexcept { return m_body.GetKernels(); }
CAmount GetTotalFee() const noexcept { return m_body.GetTotalFee(); }
int32_t GetLockHeight() const noexcept { return m_body.GetLockHeight(); }
uint64_t CalcWeight() const noexcept { return (uint64_t)Weight::Calculate(m_body); }
std::vector<Commitment> GetKernelCommits() const noexcept { return m_body.GetKernelCommits(); }
std::vector<Commitment> GetInputCommits() const noexcept { return m_body.GetInputCommits(); }
std::vector<Commitment> GetOutputCommits() const noexcept { return m_body.GetOutputCommits(); }
std::vector<PegInCoin> GetPegIns() const noexcept { return m_body.GetPegIns(); }
CAmount GetPegInAmount() const noexcept { return m_body.GetPegInAmount(); }
std::vector<PegOutCoin> GetPegOuts() const noexcept { return m_body.GetPegOuts(); }
CAmount GetSupplyChange() const noexcept { return m_body.GetSupplyChange(); }
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(Transaction, obj)
{
READWRITE(obj.m_kernelOffset);
READWRITE(obj.m_stealthOffset);
READWRITE(obj.m_body);
SER_READ(obj, obj.m_hash = Hashed(obj));
if (ser_action.ForRead()) {
if (obj.m_body.GetKernels().empty()) {
throw std::ios_base::failure("Transaction requires at least one kernel");
}
}
}
//
// Traits
//
std::string Format() const final { return "Tx(" + GetHash().Format() + ")"; }
const mw::Hash& GetHash() const noexcept final { return m_hash; }
bool IsStandard() const noexcept;
void Validate() const;
std::string Print() const noexcept
{
auto print_kernel = [](const Kernel& kernel) -> std::string {
return StringUtil::Format(
"kern(kernel_id:{}, commit:{}, pegin: {}, pegout: {}, fee: {})",
kernel.GetKernelID(),
kernel.GetCommitment(),
kernel.GetPegIn(),
kernel.GetPegOutAmount(),
kernel.GetFee()
);
};
std::string kernels_str = std::accumulate(
GetKernels().begin(), GetKernels().end(), std::string{},
[&print_kernel](std::string str, const Kernel& kern) {
return str.empty() ? print_kernel(kern) : std::move(str) + ", " + print_kernel(kern);
}
);
return StringUtil::Format(
"tx(hash:{}, offset:{}, kernels:[{}], inputs:{}, outputs:{})",
GetHash(),
GetKernelOffset().ToHex(),
kernels_str,
GetInputCommits(),
GetOutputCommits()
);
}
private:
// The kernel "offset" k2 excess is k1G after splitting the key k = k1 + k2.
BlindingFactor m_kernelOffset;
BlindingFactor m_stealthOffset;
// The transaction body.
TxBody m_body;
mw::Hash m_hash;
};
END_NAMESPACE

View File

@ -0,0 +1,116 @@
#pragma once
// Copyright (c) 2018-2019 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Logger.h>
#include <mw/common/Traits.h>
#include <mw/models/tx/Input.h>
#include <mw/models/tx/Output.h>
#include <mw/models/tx/Kernel.h>
#include <mw/models/tx/PegInCoin.h>
#include <mw/models/tx/PegOutCoin.h>
#include <mw/crypto/Bulletproofs.h>
#include <mw/crypto/Schnorr.h>
#include <memory>
#include <vector>
////////////////////////////////////////
// TRANSACTION BODY - Container for all inputs, outputs, and kernels in a transaction or block.
////////////////////////////////////////
class TxBody : public Traits::ISerializable
{
public:
using CPtr = std::shared_ptr<const TxBody>;
//
// Constructors
//
TxBody(std::vector<Input> inputs, std::vector<Output> outputs, std::vector<Kernel> kernels)
: m_inputs(std::move(inputs)), m_outputs(std::move(outputs)), m_kernels(std::move(kernels)) { }
TxBody(const TxBody& other) = default;
TxBody(TxBody&& other) noexcept = default;
TxBody() = default;
//
// Destructor
//
virtual ~TxBody() = default;
//
// Operators
//
TxBody& operator=(const TxBody& other) = default;
TxBody& operator=(TxBody&& other) noexcept = default;
bool operator==(const TxBody& rhs) const noexcept
{
return
m_inputs == rhs.m_inputs &&
m_outputs == rhs.m_outputs &&
m_kernels == rhs.m_kernels;
}
//
// Getters
//
const std::vector<Input>& GetInputs() const noexcept { return m_inputs; }
const std::vector<Output>& GetOutputs() const noexcept { return m_outputs; }
const std::vector<Kernel>& GetKernels() const noexcept { return m_kernels; }
std::vector<Commitment> GetKernelCommits() const noexcept { return Commitments::From(m_kernels); }
std::vector<Commitment> GetInputCommits() const noexcept { return Commitments::From(m_inputs); }
std::vector<Commitment> GetOutputCommits() const noexcept { return Commitments::From(m_outputs); }
std::vector<mw::Hash> GetKernelIDs() const noexcept { return Hashes::From(m_kernels); }
std::vector<mw::Hash> GetSpentIDs() const noexcept
{
std::vector<mw::Hash> output_ids;
std::transform(
m_inputs.cbegin(), m_inputs.cend(),
std::back_inserter(output_ids),
[](const Input& input) { return input.GetOutputID(); });
return output_ids;
}
std::vector<mw::Hash> GetOutputIDs() const noexcept { return Hashes::From(m_outputs); }
std::vector<PublicKey> GetStealthExcesses() const noexcept {
std::vector<PublicKey> stealth_excesses;
for (const Kernel& kernel : m_kernels) {
if (kernel.HasStealthExcess()) {
stealth_excesses.push_back(kernel.GetStealthExcess());
}
}
return stealth_excesses;
}
std::vector<PegInCoin> GetPegIns() const noexcept;
CAmount GetPegInAmount() const noexcept;
std::vector<PegOutCoin> GetPegOuts() const noexcept;
CAmount GetTotalFee() const noexcept;
CAmount GetSupplyChange() const noexcept;
int32_t GetLockHeight() const noexcept;
//
// Serialization/Deserialization
//
IMPL_SERIALIZABLE(TxBody, obj)
{
READWRITE(obj.m_inputs, obj.m_outputs, obj.m_kernels);
}
void Validate() const;
private:
// List of inputs spent by the transaction.
std::vector<Input> m_inputs;
// List of outputs the transaction produces.
std::vector<Output> m_outputs;
// List of kernels that make up this transaction.
std::vector<Kernel> m_kernels;
};

View File

@ -0,0 +1,37 @@
#pragma once
#include <mw/common/Macros.h>
#include <mw/common/Traits.h>
#include <mw/models/tx/Output.h>
#include <mw/mmr/LeafIndex.h>
#include <serialize.h>
class UTXO : public Traits::ISerializable
{
public:
using CPtr = std::shared_ptr<const UTXO>;
UTXO() : m_blockHeight(0), m_leafIdx(), m_output() { }
UTXO(const int32_t blockHeight, mmr::LeafIndex leafIdx, Output output)
: m_blockHeight(blockHeight), m_leafIdx(std::move(leafIdx)), m_output(std::move(output)) { }
int32_t GetBlockHeight() const noexcept { return m_blockHeight; }
const mmr::LeafIndex& GetLeafIndex() const noexcept { return m_leafIdx; }
const Output& GetOutput() const noexcept { return m_output; }
const mw::Hash& GetOutputID() const noexcept { return m_output.GetOutputID(); }
const Commitment& GetCommitment() const noexcept { return m_output.GetCommitment(); }
const PublicKey& GetReceiverPubKey() const noexcept { return m_output.GetReceiverPubKey(); }
const RangeProof::CPtr& GetRangeProof() const noexcept { return m_output.GetRangeProof(); }
ProofData BuildProofData() const noexcept { return m_output.BuildProofData(); }
IMPL_SERIALIZABLE(UTXO, obj)
{
READWRITE(obj.m_blockHeight, obj.m_leafIdx, obj.m_output);
}
private:
int32_t m_blockHeight;
mmr::LeafIndex m_leafIdx;
Output m_output;
};

View File

@ -0,0 +1,57 @@
#pragma once
#include <mw/common/Macros.h>
#include <mw/models/crypto/BlindingFactor.h>
#include <mw/models/crypto/Commitment.h>
#include <amount.h>
#include <boost/optional.hpp>
MW_NAMESPACE
/// <summary>
/// Change outputs will use the stealth address generated using index 2,000,000.
/// </summary>
static constexpr uint32_t CHANGE_INDEX{0};
/// <summary>
/// Peg-in outputs will use the stealth address generated using index 2,000,000.
/// </summary>
static constexpr uint32_t PEGIN_INDEX{1};
/// <summary>
/// Represents an output owned by the wallet, and the keys necessary to spend it.
/// </summary>
struct Coin : public Traits::ISerializable {
// Index of the subaddress this coin was received at.
uint32_t address_index;
// The private key needed in order to spend the coin.
// May be empty for watch-only wallets.
boost::optional<SecretKey> key;
// The blinding factor needed in order to spend the coin.
// May be empty for watch-only wallets.
boost::optional<BlindingFactor> blind;
// The output amount in litoshis.
// Typically positive, but could be 0 in the future when we start using decoys to improve privacy.
CAmount amount;
// The output's ID (hash).
mw::Hash output_id;
bool IsChange() const noexcept { return address_index == CHANGE_INDEX; }
bool IsPegIn() const noexcept { return address_index == PEGIN_INDEX; }
IMPL_SERIALIZABLE(Coin, obj)
{
READWRITE(VARINT(obj.address_index));
READWRITE(obj.key);
READWRITE(obj.blind);
READWRITE(VARINT_MODE(obj.amount, VarIntMode::NONNEGATIVE_SIGNED));
READWRITE(obj.output_id);
}
};
END_NAMESPACE

View File

@ -0,0 +1,103 @@
#pragma once
// Copyright (c) 2018-2020 David Burkett
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
#include <mw/common/Traits.h>
#include <mw/exceptions/DeserializationException.h>
#include <cstdint>
#include <string>
#include <vector>
static const uint32_t MINIMUM_HARDENED_INDEX = 0x80000000;
class KeyChainPath : public Traits::IPrintable
{
public:
KeyChainPath(std::vector<uint32_t> keyIndices)
: m_keyIndices(std::move(keyIndices)) { }
//
// Operators
//
bool operator<(const KeyChainPath& other) const { return GetKeyIndices() < other.GetKeyIndices(); }
bool operator==(const KeyChainPath& other) const { return GetKeyIndices() == other.GetKeyIndices(); }
bool operator!=(const KeyChainPath& other) const { return GetKeyIndices() != other.GetKeyIndices(); }
const std::vector<uint32_t>& GetKeyIndices() const { return m_keyIndices; }
/// <summary>
/// Example format: m/0/2'/500
/// </summary>
/// <returns>The formatted BIP32 keychain path.</returns>
std::string Format() const final
{
std::string path = "m";
for (const uint32_t keyIndex : m_keyIndices) {
path += "/";
if (keyIndex < MINIMUM_HARDENED_INDEX) {
path += std::to_string(keyIndex);
} else {
path += std::to_string(keyIndex - MINIMUM_HARDENED_INDEX) + "'";
}
}
return path;
}
static KeyChainPath FromString(const std::string& path)
{
if (path.empty() || path.at(0) != 'm') {
ThrowDeserialization_F("Invalid path: {}", path);
}
std::vector<uint32_t> indices;
std::vector<std::string> str_indices = StringUtil::Split(path, "/");
for (size_t i = 1; i < str_indices.size(); i++) {
uint32_t key_index = 0;
std::string str_index = str_indices[i];
if (str_index.empty()) {
ThrowDeserialization_F("Invalid path: {}", path);
}
if (str_index.back() == '\'') {
key_index = MINIMUM_HARDENED_INDEX;
str_index = str_index.substr(0, str_index.size() - 1);
}
key_index += std::stoul(str_index);
indices.push_back(key_index);
}
return KeyChainPath(std::move(indices));
}
KeyChainPath GetFirstChild() const
{
std::vector<uint32_t> keyIndicesCopy = m_keyIndices;
keyIndicesCopy.push_back(0);
return KeyChainPath(std::move(keyIndicesCopy));
}
KeyChainPath GetChild(const uint32_t childIndex) const
{
std::vector<uint32_t> keyIndicesCopy = m_keyIndices;
keyIndicesCopy.push_back(childIndex);
return KeyChainPath(std::move(keyIndicesCopy));
}
KeyChainPath GetNextSibling() const
{
assert(!m_keyIndices.empty());
std::vector<uint32_t> keyIndicesCopy = m_keyIndices;
keyIndicesCopy.back()++;
return KeyChainPath(std::move(keyIndicesCopy));
}
private:
std::vector<uint32_t> m_keyIndices;
};

View File

@ -0,0 +1,14 @@
#pragma once
#include <mw/common/Macros.h>
#include <mw/models/wallet/StealthAddress.h>
#include <amount.h>
MW_NAMESPACE
struct Recipient {
CAmount amount;
StealthAddress address;
};
END_NAMESPACE

View File

@ -0,0 +1,47 @@
#pragma once
#include <mw/common/Traits.h>
#include <mw/crypto/Keys.h>
#include <mw/models/crypto/PublicKey.h>
class StealthAddress : public Traits::ISerializable
{
public:
StealthAddress() = default;
StealthAddress(PublicKey&& scan, PublicKey&& spend)
: m_scan(std::move(scan)), m_spend(std::move(spend)) { }
StealthAddress(const PublicKey& scan, const PublicKey& spend)
: m_scan(scan), m_spend(spend) { }
bool operator==(const StealthAddress& rhs) const noexcept
{
return m_scan == rhs.m_scan && m_spend == rhs.m_spend;
}
bool operator<(const StealthAddress& rhs) const noexcept
{
if (m_scan < rhs.m_scan) return true;
if (m_scan != rhs.m_scan) return false;
return m_spend < rhs.m_spend;
}
static StealthAddress Random()
{
return StealthAddress(Keys::Random().PubKey(), Keys::Random().PubKey());
}
const PublicKey& A() const noexcept { return m_scan; }
const PublicKey& B() const noexcept { return m_spend; }
const PublicKey& GetScanPubKey() const noexcept { return m_scan; }
const PublicKey& GetSpendPubKey() const noexcept { return m_spend; }
IMPL_SERIALIZABLE(StealthAddress, obj)
{
READWRITE(obj.m_scan, obj.m_spend);
}
private:
PublicKey m_scan;
PublicKey m_spend;
};

View File

@ -0,0 +1,36 @@
#pragma once
#include <mw/common/Macros.h>
#include <mw/models/tx/Transaction.h>
#include <mw/models/tx/PegInCoin.h>
#include <mw/node/CoinsView.h>
#include <memory>
MW_NAMESPACE
class BlockBuilder
{
public:
using Ptr = std::shared_ptr<BlockBuilder>;
/// <summary>
/// Constructs a new BlockBuilder for assembling an MW ext block incrementally (tx by tx).
/// </summary>
/// <param name="height">The height of the block being built.</param>
/// <param name="view">The CoinsView representing the latest state of the active chain. Must not be null.</param>
/// <returns>A non-null BlockBuilder</returns>
BlockBuilder(const uint64_t height, const mw::ICoinsView::Ptr& pCoinsView)
: m_height(height), m_weight(0), m_pCoinsView(std::make_shared<mw::CoinsViewCache>(pCoinsView)), m_pAggregated(nullptr){ }
bool AddTransaction(const Transaction::CPtr& pTransaction, const std::vector<PegInCoin>& pegins);
mw::Block::Ptr BuildBlock() const;
private:
uint64_t m_height;
uint64_t m_weight;
mw::CoinsViewCache::Ptr m_pCoinsView;
Transaction::CPtr m_pAggregated;
};
END_NAMESPACE // mw

View File

@ -0,0 +1,26 @@
#pragma once
#include <mw/models/block/Block.h>
class BlockValidator
{
public:
BlockValidator() = default;
static bool ValidateBlock(
const mw::Block::CPtr& pBlock,
const std::vector<PegInCoin>& pegInCoins,
const std::vector<PegOutCoin>& pegOutCoins
) noexcept;
private:
static void ValidatePegInCoins(
const mw::Block::CPtr& pBlock,
const std::vector<PegInCoin>& pegInCoins
);
static void ValidatePegOutCoins(
const mw::Block::CPtr& pBlock,
const std::vector<PegOutCoin>& pegOutCoins
);
};

Some files were not shown because too many files have changed in this diff Show More