MWEB: Add libmw
This commit is contained in:
parent
e735822f3d
commit
db90d67828
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
9
src/libmw/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
out/
|
||||
build/
|
||||
bin/
|
||||
build-aux/
|
||||
|
||||
.vs/
|
||||
CMakeSettings.json
|
||||
libmw-config.h
|
||||
stamp-h1
|
||||
21
src/libmw/LICENSE
Normal file
21
src/libmw/LICENSE
Normal 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
45
src/libmw/README.md
Normal 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/
|
||||
29
src/libmw/deps/caches/LICENSE.md
Normal file
29
src/libmw/deps/caches/LICENSE.md
Normal 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.
|
||||
68
src/libmw/deps/caches/README.md
Normal file
68
src/libmw/deps/caches/README.md
Normal file
@ -0,0 +1,68 @@
|
||||
[](https://travis-ci.org/vpetrigo/caches)
|
||||
[](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.
|
||||
11
src/libmw/deps/caches/include/caches/Cache.h
Normal file
11
src/libmw/deps/caches/include/caches/Cache.h
Normal 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>>;
|
||||
153
src/libmw/deps/caches/include/caches/internal/cache.hpp
Normal file
153
src/libmw/deps/caches/include/caches/internal/cache.hpp
Normal 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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
19
src/libmw/deps/ghc/LICENSE
Normal file
19
src/libmw/deps/ghc/LICENSE
Normal 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.
|
||||
937
src/libmw/deps/ghc/README.md
Normal file
937
src/libmw/deps/ghc/README.md
Normal file
@ -0,0 +1,937 @@
|
||||

|
||||

|
||||
[](https://travis-ci.org/gulrak/filesystem)
|
||||
[](https://ci.appveyor.com/project/gulrak/filesystem)
|
||||
[](https://cirrus-ci.com/github/gulrak/filesystem)
|
||||
[](https://cloud.drone.io/gulrak/filesystem)
|
||||
[](https://coveralls.io/github/gulrak/filesystem?branch=master)
|
||||
[](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.
|
||||
4969
src/libmw/deps/ghc/include/ghc/filesystem.hpp
Normal file
4969
src/libmw/deps/ghc/include/ghc/filesystem.hpp
Normal file
File diff suppressed because it is too large
Load Diff
46
src/libmw/deps/ghc/include/ghc/fs_fwd.hpp
Normal file
46
src/libmw/deps/ghc/include/ghc/fs_fwd.hpp
Normal 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
|
||||
43
src/libmw/deps/ghc/include/ghc/fs_impl.hpp
Normal file
43
src/libmw/deps/ghc/include/ghc/fs_impl.hpp
Normal 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>
|
||||
59
src/libmw/deps/ghc/include/ghc/fs_std.hpp
Normal file
59
src/libmw/deps/ghc/include/ghc/fs_std.hpp
Normal 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
|
||||
|
||||
63
src/libmw/deps/ghc/include/ghc/fs_std_fwd.hpp
Normal file
63
src/libmw/deps/ghc/include/ghc/fs_std_fwd.hpp
Normal 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
|
||||
|
||||
46
src/libmw/deps/ghc/include/ghc/fs_std_impl.hpp
Normal file
46
src/libmw/deps/ghc/include/ghc/fs_std_impl.hpp
Normal 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
|
||||
|
||||
1760
src/libmw/deps/mio/include/mio/mmap.hpp
Normal file
1760
src/libmw/deps/mio/include/mio/mmap.hpp
Normal file
File diff suppressed because it is too large
Load Diff
104
src/libmw/include/mw/common/BitSet.h
Normal file
104
src/libmw/include/mw/common/BitSet.h
Normal 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);
|
||||
}
|
||||
};
|
||||
220
src/libmw/include/mw/common/Lock.h
Normal file
220
src/libmw/include/mw/common/Lock.h
Normal 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;
|
||||
};
|
||||
71
src/libmw/include/mw/common/Logger.h
Normal file
71
src/libmw/include/mw/common/Logger.h
Normal 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__)
|
||||
6
src/libmw/include/mw/common/Macros.h
Normal file
6
src/libmw/include/mw/common/Macros.h
Normal 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 }
|
||||
76
src/libmw/include/mw/common/Traits.h
Normal file
76
src/libmw/include/mw/common/Traits.h
Normal 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;
|
||||
};
|
||||
}
|
||||
66
src/libmw/include/mw/consensus/Aggregation.h
Normal file
66
src/libmw/include/mw/consensus/Aggregation.h
Normal 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)
|
||||
);
|
||||
}
|
||||
};
|
||||
111
src/libmw/include/mw/consensus/KernelSumValidator.h
Normal file
111
src/libmw/include/mw/consensus/KernelSumValidator.h
Normal 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
15
src/libmw/include/mw/consensus/Params.h
Normal file
15
src/libmw/include/mw/consensus/Params.h
Normal 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
|
||||
63
src/libmw/include/mw/consensus/StealthSumValidator.h
Normal file
63
src/libmw/include/mw/consensus/StealthSumValidator.h
Normal 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
97
src/libmw/include/mw/consensus/Weight.h
Normal file
97
src/libmw/include/mw/consensus/Weight.h
Normal 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;
|
||||
}
|
||||
};
|
||||
86
src/libmw/include/mw/crypto/Blinds.h
Normal file
86
src/libmw/include/mw/crypto/Blinds.h
Normal 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;
|
||||
};
|
||||
33
src/libmw/include/mw/crypto/Bulletproofs.h
Normal file
33
src/libmw/include/mw/crypto/Bulletproofs.h
Normal 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
|
||||
);
|
||||
};
|
||||
78
src/libmw/include/mw/crypto/Hasher.h
Normal file
78
src/libmw/include/mw/crypto/Hasher.h
Normal 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();
|
||||
}
|
||||
61
src/libmw/include/mw/crypto/Keys.h
Normal file
61
src/libmw/include/mw/crypto/Keys.h
Normal 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;
|
||||
};
|
||||
45
src/libmw/include/mw/crypto/MuSig.h
Normal file
45
src/libmw/include/mw/crypto/MuSig.h
Normal 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
|
||||
);
|
||||
};
|
||||
54
src/libmw/include/mw/crypto/Pedersen.h
Normal file
54
src/libmw/include/mw/crypto/Pedersen.h
Normal 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);
|
||||
};
|
||||
38
src/libmw/include/mw/crypto/PublicKeys.h
Normal file
38
src/libmw/include/mw/crypto/PublicKeys.h
Normal 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);
|
||||
};
|
||||
36
src/libmw/include/mw/crypto/Schnorr.h
Normal file
36
src/libmw/include/mw/crypto/Schnorr.h
Normal 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
|
||||
);
|
||||
};
|
||||
25
src/libmw/include/mw/crypto/SecretKeys.h
Normal file
25
src/libmw/include/mw/crypto/SecretKeys.h
Normal 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;
|
||||
};
|
||||
46
src/libmw/include/mw/db/CoinDB.h
Normal file
46
src/libmw/include/mw/db/CoinDB.h
Normal 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;
|
||||
};
|
||||
24
src/libmw/include/mw/db/LeafDB.h
Normal file
24
src/libmw/include/mw/db/LeafDB.h
Normal 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;
|
||||
};
|
||||
36
src/libmw/include/mw/db/MMRInfoDB.h
Normal file
36
src/libmw/include/mw/db/MMRInfoDB.h
Normal 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;
|
||||
};
|
||||
17
src/libmw/include/mw/exceptions/CryptoException.h
Normal file
17
src/libmw/include/mw/exceptions/CryptoException.h
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
17
src/libmw/include/mw/exceptions/DatabaseException.h
Normal file
17
src/libmw/include/mw/exceptions/DatabaseException.h
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
17
src/libmw/include/mw/exceptions/DeserializationException.h
Normal file
17
src/libmw/include/mw/exceptions/DeserializationException.h
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
17
src/libmw/include/mw/exceptions/FileException.h
Normal file
17
src/libmw/include/mw/exceptions/FileException.h
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
17
src/libmw/include/mw/exceptions/InsufficientFundsException.h
Normal file
17
src/libmw/include/mw/exceptions/InsufficientFundsException.h
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
33
src/libmw/include/mw/exceptions/LTCException.h
Normal file
33
src/libmw/include/mw/exceptions/LTCException.h
Normal 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;
|
||||
};
|
||||
17
src/libmw/include/mw/exceptions/NotFoundException.h
Normal file
17
src/libmw/include/mw/exceptions/NotFoundException.h
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
74
src/libmw/include/mw/exceptions/ValidationException.h
Normal file
74
src/libmw/include/mw/exceptions/ValidationException.h
Normal 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";
|
||||
}
|
||||
};
|
||||
118
src/libmw/include/mw/file/AppendOnlyFile.h
Normal file
118
src/libmw/include/mw/file/AppendOnlyFile.h
Normal 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;
|
||||
};
|
||||
37
src/libmw/include/mw/file/File.h
Normal file
37
src/libmw/include/mw/file/File.h
Normal 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;
|
||||
};
|
||||
133
src/libmw/include/mw/file/FilePath.h
Normal file
133
src/libmw/include/mw/file/FilePath.h
Normal 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;
|
||||
};
|
||||
79
src/libmw/include/mw/file/MemMap.h
Normal file
79
src/libmw/include/mw/file/MemMap.h
Normal 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;
|
||||
};
|
||||
42
src/libmw/include/mw/interfaces/chain_interface.h
Normal file
42
src/libmw/include/mw/interfaces/chain_interface.h
Normal 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
|
||||
51
src/libmw/include/mw/interfaces/db_interface.h
Normal file
51
src/libmw/include/mw/interfaces/db_interface.h
Normal 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
|
||||
79
src/libmw/include/mw/mmr/Index.h
Normal file
79
src/libmw/include/mw/mmr/Index.h
Normal 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
|
||||
49
src/libmw/include/mw/mmr/Leaf.h
Normal file
49
src/libmw/include/mw/mmr/Leaf.h
Normal 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
|
||||
67
src/libmw/include/mw/mmr/LeafIndex.h
Normal file
67
src/libmw/include/mw/mmr/LeafIndex.h
Normal 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
|
||||
94
src/libmw/include/mw/mmr/LeafSet.h
Normal file
94
src/libmw/include/mw/mmr/LeafSet.h
Normal 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;
|
||||
};
|
||||
218
src/libmw/include/mw/mmr/MMR.h
Normal file
218
src/libmw/include/mw/mmr/MMR.h
Normal 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;
|
||||
};
|
||||
37
src/libmw/include/mw/mmr/MMRInfo.h
Normal file
37
src/libmw/include/mw/mmr/MMRInfo.h
Normal 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);
|
||||
}
|
||||
};
|
||||
44
src/libmw/include/mw/mmr/MMRUtil.h
Normal file
44
src/libmw/include/mw/mmr/MMRUtil.h
Normal 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;
|
||||
};
|
||||
31
src/libmw/include/mw/mmr/PruneList.h
Normal file
31
src/libmw/include/mw/mmr/PruneList.h
Normal 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;
|
||||
};
|
||||
129
src/libmw/include/mw/models/block/Block.h
Normal file
129
src/libmw/include/mw/models/block/Block.h
Normal 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
|
||||
51
src/libmw/include/mw/models/block/BlockUndo.h
Normal file
51
src/libmw/include/mw/models/block/BlockUndo.h
Normal 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
|
||||
196
src/libmw/include/mw/models/block/Header.h
Normal file
196
src/libmw/include/mw/models/block/Header.h
Normal 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
|
||||
192
src/libmw/include/mw/models/crypto/BigInteger.h
Normal file
192
src/libmw/include/mw/models/crypto/BigInteger.h
Normal 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;
|
||||
};
|
||||
65
src/libmw/include/mw/models/crypto/BlindingFactor.h
Normal file
65
src/libmw/include/mw/models/crypto/BlindingFactor.h
Normal 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;
|
||||
};
|
||||
113
src/libmw/include/mw/models/crypto/Commitment.h
Normal file
113
src/libmw/include/mw/models/crypto/Commitment.h
Normal 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;
|
||||
}
|
||||
};
|
||||
44
src/libmw/include/mw/models/crypto/Hash.h
Normal file
44
src/libmw/include/mw/models/crypto/Hash.h
Normal 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;
|
||||
}
|
||||
};
|
||||
23
src/libmw/include/mw/models/crypto/ProofData.h
Normal file
23
src/libmw/include/mw/models/crypto/ProofData.h
Normal 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); }
|
||||
};
|
||||
20
src/libmw/include/mw/models/crypto/ProofMessage.h
Normal file
20
src/libmw/include/mw/models/crypto/ProofMessage.h
Normal 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;
|
||||
};
|
||||
90
src/libmw/include/mw/models/crypto/PublicKey.h
Normal file
90
src/libmw/include/mw/models/crypto/PublicKey.h
Normal 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());
|
||||
}
|
||||
};
|
||||
}
|
||||
83
src/libmw/include/mw/models/crypto/RangeProof.h
Normal file
83
src/libmw/include/mw/models/crypto/RangeProof.h
Normal 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;
|
||||
};
|
||||
35
src/libmw/include/mw/models/crypto/RewoundProof.h
Normal file
35
src/libmw/include/mw/models/crypto/RewoundProof.h
Normal 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;
|
||||
};
|
||||
74
src/libmw/include/mw/models/crypto/SecretKey.h
Normal file
74
src/libmw/include/mw/models/crypto/SecretKey.h
Normal 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>;
|
||||
80
src/libmw/include/mw/models/crypto/Signature.h
Normal file
80
src/libmw/include/mw/models/crypto/Signature.h
Normal 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;
|
||||
};
|
||||
72
src/libmw/include/mw/models/crypto/SignedMessage.h
Normal file
72
src/libmw/include/mw/models/crypto/SignedMessage.h
Normal 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());
|
||||
}
|
||||
};
|
||||
}
|
||||
111
src/libmw/include/mw/models/tx/Input.h
Normal file
111
src/libmw/include/mw/models/tx/Input.h
Normal 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;
|
||||
228
src/libmw/include/mw/models/tx/Kernel.h
Normal file
228
src/libmw/include/mw/models/tx/Kernel.h
Normal 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;
|
||||
231
src/libmw/include/mw/models/tx/Output.h
Normal file
231
src/libmw/include/mw/models/tx/Output.h
Normal 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;
|
||||
41
src/libmw/include/mw/models/tx/OutputMask.h
Normal file
41
src/libmw/include/mw/models/tx/OutputMask.h
Normal 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;
|
||||
};
|
||||
43
src/libmw/include/mw/models/tx/PegInCoin.h
Normal file
43
src/libmw/include/mw/models/tx/PegInCoin.h
Normal 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;
|
||||
};
|
||||
64
src/libmw/include/mw/models/tx/PegOutCoin.h
Normal file
64
src/libmw/include/mw/models/tx/PegOutCoin.h
Normal 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;
|
||||
};
|
||||
157
src/libmw/include/mw/models/tx/Transaction.h
Normal file
157
src/libmw/include/mw/models/tx/Transaction.h
Normal 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
|
||||
116
src/libmw/include/mw/models/tx/TxBody.h
Normal file
116
src/libmw/include/mw/models/tx/TxBody.h
Normal 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;
|
||||
};
|
||||
37
src/libmw/include/mw/models/tx/UTXO.h
Normal file
37
src/libmw/include/mw/models/tx/UTXO.h
Normal 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;
|
||||
};
|
||||
57
src/libmw/include/mw/models/wallet/Coin.h
Normal file
57
src/libmw/include/mw/models/wallet/Coin.h
Normal 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
|
||||
103
src/libmw/include/mw/models/wallet/KeyChainPath.h
Normal file
103
src/libmw/include/mw/models/wallet/KeyChainPath.h
Normal 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;
|
||||
};
|
||||
14
src/libmw/include/mw/models/wallet/Recipient.h
Normal file
14
src/libmw/include/mw/models/wallet/Recipient.h
Normal 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
|
||||
47
src/libmw/include/mw/models/wallet/StealthAddress.h
Normal file
47
src/libmw/include/mw/models/wallet/StealthAddress.h
Normal 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;
|
||||
};
|
||||
36
src/libmw/include/mw/node/BlockBuilder.h
Normal file
36
src/libmw/include/mw/node/BlockBuilder.h
Normal 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
|
||||
26
src/libmw/include/mw/node/BlockValidator.h
Normal file
26
src/libmw/include/mw/node/BlockValidator.h
Normal 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
Loading…
x
Reference in New Issue
Block a user